Signals: The Reactivity Model Taking Over Frontends
Signals offer fine-grained reactivity with automatic dependency tracking — and nearly every major framework has adopted them. Here's why the model won.
A few years ago, “signals” was a term associated mostly with Solid.js and reactive programming literature. Today, Solid, Preact, Angular, Vue, and Svelte 5 all use signal-based reactivity at their core — and there’s an active TC39 proposal to add signals as a primitive to JavaScript itself. Something clearly clicked. Understanding why explains a lot about where frontend development is heading.
What a signal actually is
A signal is a reactive value container. When you read a signal’s value inside a computed or effect, that dependency is tracked automatically. When the signal changes, only the things that depend on it re-run — nothing more.
Three concepts make up the model:
- Signal — a single reactive value. Reading it registers a dependency; writing it notifies dependents.
- Computed — a derived value that recalculates automatically when its signal dependencies change.
- Effect — a side effect (DOM update, logging, fetch) that re-runs when its dependencies change.
Here’s a framework-neutral illustration of the API shape (this is generic pseudo-code, not a specific library):
// Generic signal pseudo-API — illustrative only
const count = signal(0);
const doubled = computed(() => count.get() * 2);
effect(() => {
console.log(`count is ${count.get()}, doubled is ${doubled.get()}`);
});
count.set(1);
// → "count is 1, doubled is 2"
count.set(5);
// → "count is 5, doubled is 10"
The dependency graph is built and maintained at runtime. You don’t declare what depends on what — the framework infers it by observing which signals were read during each computation.
How this differs from virtual DOM diffing
The dominant React mental model is: state changes → re-render the component tree → diff the virtual DOM → apply minimal DOM patches. It’s an elegant abstraction, but reconciliation touches the entire component subtree by default, even if only a small piece of state changed.
Signals invert this. Instead of “re-render and diff to find what changed,” signals know exactly what changed because every dependency is tracked. Updates are surgical: only the specific DOM nodes or computeds that depend on the changed signal are touched.
| Approach | Update model | Unit of re-execution |
|---|---|---|
| Virtual DOM (React) | Re-render subtree, reconcile | Component |
| Signals | Fine-grained dependency graph | Individual reactive binding |
For most apps, the difference is imperceptible. For dense UIs — data grids, real-time dashboards, animations — fine-grained reactivity removes entire categories of unnecessary work.
The convergence across frameworks
The striking thing about signals isn’t that one framework adopted them — it’s that frameworks with very different philosophies all arrived at the same model independently, then formalized it.
Solid built its entire architecture around signals from the start. There’s no virtual DOM at all; components run once and signals drive updates directly.
Preact Signals introduced signals as an opt-in addition to Preact, with hooks that integrate into the existing component model. A signal updated in one component can trigger a DOM update in another without touching the component tree at all.
Angular shipped a signals-based reactivity system as a first-class API, gradually replacing the Zone.js-based change detection that long-time Angular developers know well. It’s a significant architectural shift for a mature framework.
Vue’s reactivity system — ref, computed, and watchEffect — has always been signal-shaped. Vue 3’s Composition API made this more explicit and compositional.
Svelte 5 runes reworked Svelte’s compiler-driven reactivity around an explicit signal primitive, replacing the implicit $: reactive declarations of earlier versions with $state and $derived runes that the compiler understands deeply.
Five frameworks, five different constraints, the same core model.
The TC39 signals proposal
The convergence wasn’t lost on the standards community. There is an active TC39 proposal to add a signals primitive to JavaScript itself — not as a DOM API, but as a language-level building block that frameworks could implement on top of.
The goal isn’t to dictate how frameworks expose signals to developers; it’s to provide a shared, interoperable foundation so that signals from different libraries can compose without impedance mismatches. If a signal-aware form library and a signal-aware router are both built on the same TC39 primitive, they can share reactive state without adapters.
The proposal is in active development and hasn’t reached a stage that guarantees inclusion, but the fact that framework authors from competing projects are collaborating on it signals (yes) genuine industry consensus.
When fine-grained reactivity matters most
Signals shine in situations where:
- Many small pieces of state update frequently — a list that re-sorts on keypress, a spreadsheet cell recalculating, a live price feed
- State is shared across distant components — signals can cross component boundaries without prop drilling or context overhead
- You want predictable performance — the dependency graph makes it straightforward to reason about what will and won’t re-run
For simpler, content-heavy applications, the difference is smaller. This connects to the broader trend of React Server Components — if much of your UI never needs client-side reactivity at all, the question of which reactivity model you use becomes less critical than how much client JS you ship.
For the remaining interactive pieces, though, signals give you a precise tool: update exactly what changed, nothing else. That’s a meaningful property when you’re optimizing for CSS Grid vs. Flexbox layout performance or any other place where rendering overhead matters.
The takeaway
Signals won because they solve a real problem directly: they track dependencies automatically and update surgically. The virtual DOM diffing model works well and will continue to, but it carries overhead that signals simply don’t have in the same places. The broad adoption across Solid, Preact, Angular, Vue, and Svelte — and the TC39 proposal — means signals are no longer a niche idea. They’re the reactivity model the industry is converging on. Whether you pick them up through a new framework or through a library like Preact Signals layered onto an existing codebase, the core concept is worth understanding: signals, computeds, and effects form a minimal, composable model for reactive state that scales from a single input to an entire application.
Keep reading
Takina · · 4 min read Solid.js vs React: Two Models of Reactivity Compared
Solid.js uses fine-grained signals and no virtual DOM; React re-renders components and diffs. How the two reactivity models differ in practice.
Takina · · 4 min read Vue vs React: Which Framework Fits Your Project
Vue uses a template syntax with a reactive proxy system; React uses JSX with a virtual DOM. How the two frameworks differ and when to pick each.
Takina · · 5 min read Svelte vs React: Which Should You Choose?
Svelte compiles away at build time; React ships a runtime and virtual DOM. Bundle size, reactivity model, and ecosystem tradeoffs compared.