---
title: "bind:"
description: "Tutorial — two-way bind form elements to state with the bind: template syntax."
order: 104
---

import { Preview } from "$lib/components/preview";

export const example = `import ilha from "ilha";
import { Button, Input, Label } from "areia";

export default ilha
  .state("count", 0)
  .derived("doubled", ({ state }) => state.count() * 2)
  .on("[data-action=increase]@click", ({ state }) => {
    state.count(state.count() + 1);
  })
  .render(({ state, derived }) => (
    <>
      <p>Count: {state.count()}</p>
      <p>Doubled: {derived.doubled()}</p>
      <Input
        id="count"
        type="number"
        label="Current count"
        bind:value={state.count}
      />
      <Button variant="primary" data-action="increase">
        Increase
      </Button>
    </>
  ));
`

# Bind

Use the `bind:` template syntax in JSX to create a two-way connection between a state signal and a form element. When the state changes, the input updates. When the user types, the state updates — no event listener boilerplate required.

```ts
<input bind:value={state.count} />;
```

The `bind:` prefix goes on the attribute, and the value is a signal accessor — either a local `.state()` accessor or an external `signal()`. Ilha infers the correct value property automatically: `value` for text and number inputs, `checked` for checkboxes.

Because binding is declared directly in the template, it lives right next to the element it controls. In the example above, the input and the button both control the same `count` state — either can update it, and both stay in sync.

<Preview code={example} size="lg" />

## Similar concepts

- React: controlled inputs with `onChange` + `value`
- Vue: `v-model`
- Svelte: `bind:value`
