morphState creates reactive state that triggers re-renders when updated.
import { morphState } from 'morph'
export default function App() {
const [count, setCount] = morphState(0)
return (
<body>
<div>Count: {count}</div>
<button onClick={() => setCount(count + 1)}>+1</button>
</body>
)
}morphState(initial) returns a tuple [state, setState]:
state — the current value (read-only)setState(value) — updates the value and triggers a re-renderPass a direct value:
setCount(5)
setName("hello")Or pass an updater function:
setCount(prev => prev + 1)Each morphState call is independent. Use multiple for different pieces of state:
const [name, setName] = morphState("")
const [age, setAge] = morphState(0)
const [active, setActive] = morphState(false)State updates are batched. Clicking a button that calls multiple setters will re-render once:
function reset() {
setName("")
setAge(0)
setActive(false)
// re-renders once with all three values updated
}TypeScript annotations work naturally:
const [count, setCount] = morphState<number>(0)
const [name, setName] = morphState<string>("")
const [items, setItems] = morphState<string[]>([])The type is usually inferred from the initial value:
const [count, setCount] = morphState(0) // inferred as number
const [open, setOpen] = morphState(true) // inferred as booleanUse morphShared for state shared across components within the same window. Export the binding from
a .mx/.ts/.tsx module, then import its getter/setter wherever it is
used. Each window gets an isolated copy; use morphEvent for cross-window
communication:
// cart.ts
import { morphShared } from 'morph'
export const [count, setCount] = morphShared<number>(0)// CartBadge.tsx
import { count } from './cart'
export function CartBadge() {
return <text>{count}</text>
}The store identity is the exporting module plus the binding name. The same getter in another file is a different store, while importing the same local name from two modules is an ambiguity error. Shared bindings must be declared at module scope and exported.
morphShared at module scope and export it (mx-shared-scope).morphShared<T>(initialValue).Use state values anywhere in JSX:
<div className={active ? "active" : "inactive"}>
{count > 0 && <span>Positive</span>}
<div style={{ width: bodyWidth }}>
{loading ? "Loading..." : "Done"}
</div>
</div>const [showDetails, setShowDetails] = morphState(false)
return (
<div>
<button onClick={() => setShowDetails(!showDetails)}>
Toggle
</button>
{showDetails && <div>Details here</div>}
</div>
)Signal<T> in the C++ runtime (runtime/cpp/reactivity/signal.h)morphState call creates a new signal with a unique IDsetState calls in the same event handler tick are coalesced into a single re-render