let and const — the compiler transforms them into signals and computed values. When data changes, only the specific DOM nodes that depend on it are updated.
State with let
Declare local state with let. The compiler transforms it into a signal.
let count = 0 becomes const count = signal(0), and count++ becomes count.value++. You never see or write this — the compiler handles it.
What the compiler transforms
| You write | Compiler produces |
|---|---|
let count = 0 | const count = signal(0) |
count++ | count.value++ |
count = 10 | count.value = 10 |
{count} in JSX | Reactive text node reading count.value |
Derived values with const
Declare derived values with const. If the expression depends on a signal, the compiler wraps it in computed().
totalItems, totalPrice, isEmpty, and summary all depend on items (a signal) and wraps each one in computed(). When items changes, these recompute — and only the DOM nodes that read them update.
Rules for const
- The right side must be an expression (not a function declaration)
- The compiler only wraps it if it detects a reactive dependency
- If there’s no reactive dependency, it stays a plain
const— zero overhead
Effects with watch()
Use watch() to run side effects when a signal changes. This is for side effects only — not for deriving values.
Reactive props
When you pass an expression to a child component, the compiler generates a getter so the child receives a live binding — not a snapshot.Display component runs once. When count changes in the parent, the getter fires and only the <span> text updates. The Display function is never re-executed.
Reactive attributes
Signal-derived expressions work directly in JSX attributes:isActive updates className, aria-pressed, and style — but doesn’t touch disabled.
Batch updates
Multiple signal writes in the same synchronous block are batched automatically:batch():
Context
Share state across components without prop drilling usingcreateContext():