State & Derived

State is the reactive foundation of every Lyap scope. Declare it once, read and update it from actions and expressions, and every bound directive updates automatically.

Declaring state

Call scope.state({...}) inside a standard <script> block. Every property you declare is wrapped in a deep reactive proxy and is immediately accessible to HTML directives.

<div>
    <script>
        const userApp = Lyap.scope('userApp');
        userApp.state({
            count: 0,
            firstName: 'Alex',
            lastName: 'Smith'
        });
    </script>

    <p>Hello, <span ly-text="userApp.firstName"></span>!</p>
    <p>Count: <span ly-text="userApp.count"></span></p>
</div>
Live previewlyap

Count: · Double:

Derived values

Derived values are read-only properties computed from reactive state. They are lazy and memoized, updating only when their underlying signal dependencies change.

<div>
    <script>
        const profile = Lyap.scope('profile');
        profile.state({
            firstName: 'Alex',
            lastName: 'Smith'
        });

        profile.derived({
            fullName: () => `${profile.firstName} ${profile.lastName}`
        });
    </script>

    <p>User: <span ly-text="profile.fullName"></span></p>
</div>
Live previewlyap

Hello, !

Read-only contract: Derived properties are computed on demand. Mutating a derived property directly is an error; mutate the upstream state instead.

Actions

Define action methods using scope.actions({...}). Action handlers execute with this bound to the reactive state proxy, allowing clean in-place mutations and async operations.

<div>
    <script>
        const counter = Lyap.scope('counter');
        counter.state({ count: 0 });
        counter.actions({
            increment() {
                this.count++;
            },
            reset() {
                this.count = 0;
            }
        });
    </script>

    <p>Count: <span ly-text="counter.count"></span></p>
    <button ly-on:click="counter.increment()">Increment</button>
    <button ly-on:click="counter.reset()">Reset</button>
</div>

Safe sandboxed expressions

Directives evaluate sandboxed expressions without invoking eval() or new Function(). Property paths, arithmetic, ternary conditionals, and method invocations are parsed securely.

<p ly-text="app.count * 2"></p>
<p ly-text="app.fullName.toUpperCase()"></p>
<p ly-text="app.price > 100 ? 'expensive' : 'cheap'"></p>
<p ly-text="app.user.avatarUrl"></p>
<button ly-bind:disabled="app.isSaving || !app.isValid">Save</button>
© 2026 Lyap — lightweight HTML enhancement. v · Built with Lyap · GitHub