Reactive State
Create and update reactive state in Hydroxide
Frameworks like React re-run your entire component when state changes, then diff the output to find what changed. Hydroxide skips all of that:
- No re-renders - Components run once per instance
- No diffing - Updates go directly to the DOM nodes that needs to be updated
- No dependency arrays - Hydroxide tracks dependencies automatically - no need to specify them manually
Reactives
A reactive is a container for a value that can notify the system when it changes. Create one with reactive():
import { reactive } from 'hydroxide'
const count = reactive(0)
const name = reactive('Alice')
const user = reactive({ name: 'Bob', age: 25 })Reading: Call the reactive as a function to get its current value:
The reason why its a function is because the framework needs to know when the reactive value is being read. It being a function allows the framework to track when and how the value is being read to create a dependency graph.
console.log(count()) // 0
console.log(name()) // 'Alice'Writing: Use set() to replace the value, or do() to transform it:
count.set(10) // replace with 10
count.do(v => v + 1) // increment by 1When you read a reactive inside JSX, Hydroxide automatically tracks it. When the value changes, only that part of the DOM updates:
function Counter() {
const count = reactive(0)
function increment() {
count.do(v => v + 1)
}
return (
<button on-click={increment}>
Count is {count()} {/* <- only this text node will be updated when `count` updates */}
</button>
)
}import { reactive } from 'hydroxide'; function Example() { const count = reactive(0); function increment() { count.set(count() + 1); } // open the console to see the logs // when state is updated, component does not re-render console.log('No renders!') return ( <button on-click={increment} class="primary-button"> count is {count()} </button> ); } export default Example;
Effects
Effects run side effects once on mount, and again whenever their tracked reactive values change:
import { reactive, effect } from 'hydroxide'
const count = reactive(0)
effect(() => {
console.log('count is now', count())
})The effect runs immediately when created, and Hydroxide tracks which reactives you read inside it. When any of them change, the effect re-runs.
import { reactive, effect } from 'hydroxide'; function Example() { const countA = reactive(0); const countB = reactive(0); // Only tracks countA - ignores countB changes // open the console to see the logs effect(() => { console.log('Counter A updated:', countA()); }); function incrementA() { countA.set(countA() + 1); } function incrementB() { countB.set(countB() + 1); } return ( <div class="container"> <button on-click={incrementA} class="primary-button"> A is {countA()} </button> <button on-click={incrementB} class="primary-button"> B is {countB()} </button> </div> ); } export default Example;
What Gets Tracked
Tracking only happens in specific contexts:
| Context | Tracked? |
|---|---|
JSX expressions {count()} | ✅ Yes |
Inside effect() | ✅ Yes |
| Regular function body | ❌ No |
function Counter() {
const count = reactive(0)
// Not tracked - runs once at setup
console.log('Initial:', count())
// Tracked - runs once on mount, then on every change
effect(() => {
console.log('Updated:', count())
})
// Tracked - updates the DOM
return <p>Count: {count()}</p>
}Shared State
Reactives can be created outside of components. Components can use them directly. When a reactive is updated - all the components that use it will be updated.
import { reactive } from 'hydroxide'; // State created outside components - shared globally const count = reactive(0); function DisplayA() { return ( <div class="card"> <span class="label">Display A</span> <span class="value">{count()}</span> </div> ); } function DisplayB() { return ( <div class="card"> <span class="label">Display B</span> <span class="value">{count()}</span> </div> ); } function Controls() { return ( <div class="controls"> <button on-click={() => count.do(v => v - 1)}>−</button> <button on-click={() => count.do(v => v + 1)}>+</button> </div> ); } function App() { return ( <div class="app"> <div class="displays"> <DisplayA /> <DisplayB /> </div> <Controls /> </div> ); } export default App;