C&C Docs
Log in
Code

Reactivity — @click · x-data · x-for

After the server-rendered page hydrates, state lives in the browser. @click handlers run in the nearest x-data scope; mutating that state re-renders the owning subtree — no server roundtrip. Every demo below is live — try it.

@click counter
<div class="flex items-center gap-4" x-data="{ count: 0 }">
  <button @click="count++" class="">Increment</button>
  <span class="" x-text="count"></span>
</div>
Result
0
x-data toggle
<div x-data="{ open: false }">
  <button @click="open = !open" class="">Toggle panel</button>
  <div x-show="open" class="">Now you see me — x-show flipped by @click.</div>
</div>
Result
keyed x-for add / remove
<div x-data="{ items: [{ id: 1, label: 'alpha' }, { id: 2, label: 'beta' }], nextId: 3 }">
  <div class="flex gap-2 mb-3">
    <button @click="items.push({ id: nextId, label: 'item ' + nextId }); nextId++" class="">Add</button>
    <button @click="items.pop()" class="">Remove last</button>
  </div>
  <ul class="space-y-1">
    <li x-for="item in items" :key="item.id" class="" x-text="item.label"></li>
  </ul>
</div>
Result
  • alpha
  • beta

$watch

$watch('form.email', cb) runs cb whenever a value changes. Give it a dotted path, or a function such as () => items.length; a change deep inside an object or an array counts.

$watch on a path and on a function
<div x-data="{ form: { email: '' }, items: [], log: [] }"
     x-init="
       $watch('form.email', v => log.push('path form.email → ' + v));
       $watch(() => items.length, n => log.push('fn items.length → ' + n))
     ">
  <div class="">
    <button @click="form.email = 'a' + form.email.length + '@x.dev'" class="">Change form.email</button>
    <button @click="items.push(items.length)" class="">Push item (in place)</button>
  </div>
  <ul class="">
    <li x-for="line in log" :key="line" x-text="line"></li>
  </ul>
  <p x-show="log.length === 0" class="">watchers seeded — change something.</p>
</div>
Result

watchers seeded — change something.