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.

javascript
                
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.

javascript
                
// 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.

javascript
                
// 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

javascript
                
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.

javascript
                
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

html
                
<div state-myvar="initialValue">
    <!-- myVar is available here -->
</div>
                 
            

Example: Toggle

html
                
<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

html
                
<div state-count="0">
    <span>Current count: @{count.value}</span>
    <button on-click="count.value++">+</button>
</div>