Hooks

Reactive primitives from somedom — available in all script contexts. Server renders use them for HTML output; the client uses them for live DOM updates.

Signals API

signal

Create a reactive value:

<script>
  import { signal } from 'jamrock';

  let count = signal(0);
</script>

<button onclick={() => count.value++}>{$count}</button>

Use $count in templates to read the value. This compiles to count.value. In script code, always use count.value to read or write.

computed

Create a derived value that auto-updates when dependencies change:

<script>
  import { signal, computed } from 'jamrock';

  let a = signal(2);
  let b = signal(3);
  let sum = computed(() => a.value + b.value);
</script>

<p>Sum: {$sum}</p>

effect

Run side effects when signals change:

<script context="client">
  import { signal, effect } from 'jamrock';

  const count = signal(0);

  effect(() => {
    document.title = `Count: ${count.value}`;
  });
</script>

effect is primarily for client-side code. In SSR, signals are evaluated once for stringification.

batch

Group multiple signal updates into one:

<script>
  import { signal, batch } from 'jamrock';

  let a = signal(1);
  let b = signal(2);

  function updateBoth() {
    batch(() => {
      a.value = 10;
      b.value = 20;
    });
  }
</script>

untracked

Read signals without subscribing:

<script>
  import { signal, untracked, effect } from 'jamrock';

  let count = signal(0);

  effect(() => {
    // This effect won't re-run when count changes
    const current = untracked(() => count.value);
    console.log('Current count:', current);
  });
</script>

trap

Error boundary for effects:

<script context="client">
  import { trap, effect, signal } from 'jamrock';

  const count = signal(0);

  trap((error) => {
    console.error('Error:', error);
  });

  effect(() => {
    if (count.value < 0) throw new Error('Invalid count');
  });
</script>

ref

Create a mutable reference (useful for DOM references):

<script context="client">
  import { ref } from 'jamrock';

  const inputRef = ref(null);

  function focus() {
    inputRef.current.focus();
  }
</script>

<input bind:this={inputRef} />
<button onclick={focus}>Focus</button>

SSR vs Client Behavior

SSR Client
signal(x) Stringified as x Reactive — triggers DOM patches
computed(fn) Evaluated once Re-evaluates when deps change
effect(fn) Not executed Runs and re-runs on dep change

$$props is NOT a signal — it's the component props object. Never call .value on it.

Template Syntax

Template Compiles to
{$count} count.value
{$count + 1} count.value + 1
class="btn-{$count}" "btn-" + count.value