Forms & Binding

ly-model provides seamless two-way data synchronization across all HTML form controls — text, textarea, numeric inputs, checkboxes, radios, and selects.

Text inputs

Bind an input to a state property. User typing updates state instantly, while programmatic state mutations update the input control.

<div>
    <script>
        const formDemo = Lyap.scope('formDemo');
        formDemo.state({ username: '' });
    </script>
    <input type="text" ly-model="formDemo.username">
    <p>Hello, <span ly-text="formDemo.username"></span>!</p>
</div>
Live previewlyap

Hello, !

Modifiers

Modifiers attach to ly-model to refine synchronization behavior:

ModifierPurpose
.trimTrims whitespace from string values before updating state.
.numberParses numeric values to finite numbers.
.lazyListens for change instead of input events (syncs on blur/enter).
<input type="text"     ly-model.trim="user.username">
<input type="number"   ly-model.number="user.age">
<textarea              ly-model.lazy="user.bio"></textarea>

Checkboxes & radios

A single checkbox binds to a boolean state property. A checkbox group sharing the same ly-model target array binds to an array of selected values. Radios bind to the selected value.

<!-- Single boolean checkbox -->
<label>
    <input type="checkbox" ly-model="form.agree"> Accept Terms
</label>

<!-- Array checkbox group -->
<label><input type="checkbox" ly-model="form.interests" value="coding"> Coding</label>
<label><input type="checkbox" ly-model="form.interests" value="design"> Design</label>

<!-- Radio group -->
<label><input type="radio" ly-model="form.role" value="developer"> Developer</label>
<label><input type="radio" ly-model="form.role" value="designer"> Designer</label>

Full form with validation

Combine ly-model, derived calculations, and ly-on:submit.prevent for accessible, reactive form management.

<div>
    <script>
        const profile = Lyap.scope('profile');
        profile.state({
            username: '',
            age: 25,
            bio: '',
            agree: false,
            interests: ['coding'],
            role: 'developer'
        });

        profile.derived({
            hasErrors: () => !profile.username || profile.age < 18 || !profile.agree
        });

        profile.actions({
            handleSubmit() {
                if (this.hasErrors) return;
                alert('Saved profile for ' + this.username);
            }
        });
    </script>

    <form ly-on:submit.prevent="profile.handleSubmit()">
        <label>Username:</label>
        <input type="text" ly-model.trim="profile.username">

        <label>Age:</label>
        <input type="number" ly-model.number="profile.age">

        <label>
            <input type="checkbox" ly-model="profile.agree"> Agree to terms
        </label>

        <button type="submit" ly-bind:disabled="profile.hasErrors">Submit Profile</button>
    </form>
</div>
© 2026 Lyap — lightweight HTML enhancement. v · Built with Lyap · GitHub