Watchers
watch lets a component react to changes in its own state.
What a watcher receives
Section titled “What a watcher receives”Each watcher is keyed by a state property name and receives a context object that includes:
newValuethe incoming state value after the updateoldValuethe outgoing state value before the updateisInitialwhether the watcher is running as part of the initial mount update pass
These properties help you compare values, filter side effects, and decide how to respond to initialization, later updates, or both.
watch: { count({ newValue, oldValue, isInitial }) { console.log(newValue, oldValue, isInitial); },}interface CounterState { count: number};
watch: { count({ newValue, oldValue, isInitial }) { console.log(newValue, oldValue, isInitial); },}Initial watcher runs
Section titled “Initial watcher runs”Watchers also run during the initial mount update pass.
The isInitial property in the context object lets you decide whether a watcher should handle initialization, later updates, or both.
watch: { count({ isInitial, newValue }) { if (isInitial) return;
console.log("Count changed:", newValue); },}interface CounterState { count: number};
watch: { count({ isInitial, newValue }) { if (isInitial) return;
console.log("Count changed:", newValue); },}Watchers versus render
Section titled “Watchers versus render”render is the best fit for DOM changes on elements the component has already resolved.
render: { panel() { return { attributes: { hidden: !this.state.open, }, }; },}interface DisclosureElements { panel: HTMLElement}
render: { panel() { return { attributes: { hidden: !this.state.open, }, }; },}However, watch is where imperative DOM work that goes beyond those render outputs should be managed. That includes tasks such as: restructuring DOM nodes, reordering elements, injecting new elements, and removing existing elements.
In other words, if you need to perform DOM work that render is not meant to describe, use watch.
watch: { open({ isInitial, newValue }) { if (isInitial) return;
if (newValue) { const detailsElement = document.createElement("p"); detailsElement.textContent = "Additional details are now visible."; this.root.append(detailsElement); } else { this.root.lastElementChild?.remove(); } },}interface DisclosureState { open: boolean;}
watch: { open({ isInitial, newValue }) { if (isInitial) return;
if (newValue) { const detailsElement = document.createElement("p"); detailsElement.textContent = "Additional details are now visible."; this.root.append(detailsElement); } else { this.root.lastElementChild?.remove(); } },}When to use state watchers
Section titled “When to use state watchers”Use watch for internal side effects such as:
- logging
- analytics
- timers
- imperative integrations and DOM work
- syncing non-render concerns to state changes
A good mental model
Section titled “A good mental model”Watchers are for internal reactions. They belong to the component definition and are best when the component itself needs to respond to its own state changes.