State Management
PawaJS provides flexible ways to manage state, from fine-grained signals to inline declarative state.
Signals ($state)
For component logic, PawaJS uses $state to create reactive signals. These are fine-grained, meaning updates are precise and don't require a Virtual DOM diff.
Basic Usage
To use a signal in your template, you must pass it to useInsert.
import { html, $state, useInsert } from "pawajs";
export const Counter = () => {
// 1. Create state
const count = $state(0);
// 2. Expose to template
useInsert({ count });
// 3. Use in template with .value
return html`
<button on-click="count.value++" class="btn">
Count is: @{count.value}
</button>
`;
};
Global State
$state can be defined outside of components to create global shared state. This allows you to share data across your application easily.
// store.js
import { $state } from "pawajs";
export const globalCount = $state(0);
// Component.js
import { globalCount } from "./store";
// ...
useInsert({ globalCount });
State Persistence
Pass a string as the second argument to persist the state to localStorage. PawaJS automatically hydrates the state from storage on initialization.
// Persists to localStorage with key 'theme-pref'
const theme = $state('light', 'theme-pref');
Async State
Pass an async function to handle data fetching.
The state object automatically gains
async (loading), failed (error),
and retry() properties. But can be aysnc on server
const userData = $state(async () => {
const res = await fetch('/api/user');
return res.json();
});
useInsert({ userData });
return html`
<div if="userData.async">Loading...</div>
<div if="userData.failed">
Error loading data.
<button on-click="userData.retry()">Retry</button>
</div>
<div else="">
Welcome, @{userData.value.name}
</div>
`;
Computed State
Pass a function as the first argument and an array of dependencies as the second argument to create a computed state that updates automatically.
const count = $state(1);
// Updates whenever count changes
const double = $state(() => count.value * 2, [count]);
Batching & Time-Slicing
PawaJS employs an intelligent scheduler. State updates are asynchronous and batched. This means multiple state changes in the same tick result in a single DOM update.
The engine also uses Time-Slicing (16ms frame budget) to break heavy rendering tasks into chunks, ensuring your application stays responsive and maintains 60fps.
Inline State (state-*)
For simple interactions where you don't want to define a separate script variable, you can declare state directly on HTML elements using the state-* attribute.
The state becomes available to the element and all its children.
Syntax
<div state-myvar="initialValue">
<!-- myVar is available here -->
</div>
Example: Toggle
<div state-isopen="false" class="p-4">
<button on-click="isopen.value = !isopen.value">
Toggle Content
</button>
<div if="isopen.value" class="mt-2">
This content is controlled by inline state!
</div>
</div>
Example: Counter
<div state-count="0">
<span>Current count: @{count.value}</span>
<button on-click="count.value++">+</button>
</div>