1. Introduction
A few weeks ago, I was deep into optimizing a React dashboard for one of my freelance clients. The project looked clean on the surface — reusable components, proper state management with hooks, and even some memoization to avoid unnecessary re-renders.
But something felt off.
As I added new widgets to the dashboard — live stats, graphs, real-time notifications — the UI started lagging. Even minor state changes triggered re-renders in places I didn’t expect. Debugging became a game of tracing useEffect
, checking dependency arrays, and managing stale closures. Every performance improvement felt like a patch on a larger architectural inefficiency.
The main pain point?
- State was distributed across deeply nested components.
-
useState
was everywhere. -
useEffect
chains were hard to track. - Memoization (
React.memo
,useCallback
) felt like fighting React itself.
That’s when I came across an article that mentioned something called Signals. I had heard the term before in Solid.js, but never gave it serious thought. But this time, I decided to dig deeper.
And honestly, it changed how I think about reactivity in UI development.
2. What Are Signals?
Let’s start simple.
The Layman Explanation
Imagine you have a value — say, a counter. Now imagine any place that uses this counter automatically updates when the value changes, without you writing any special logic for re-rendering or managing dependencies.
That’s a Signal.
You don’t need useState
, useEffect
, or even memo
. Signals just work — they’re reactive by nature. When a signal’s value changes, everything subscribed to it reacts automatically and precisely.
The Technical Definition
A Signal is a reactive primitive — a small unit of state — that notifies dependents when it changes. It doesn’t trigger component-level re-renders like React state does. Instead, it updates only the part of the DOM that depends on it.
This fine-grained reactivity is what makes Signals so powerful.
Code Comparison: React vs Signals
Let me show you a simple example — a counter.
React Version:
// Counter.jsx
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
Now with Signals (using Preact Signals):
// Counter.jsx
import { signal } from "@preact/signals-react";
const count = signal(0);
export default function Counter() {
return (
<div>
<p>Count: {count.value}</p>
<button onClick={() => count.value++}>Increment</button>
</div>
);
}
What's Different:
- No hooks.
- No state per component — the signal exists outside and still works.
- Only the exact DOM node (
<p>
) updates when the signal changes.
How It Feels Different
Using signals felt… surprisingly natural.
- The code is cleaner — no boilerplate hooks.
- It’s predictable — changes propagate where they’re used.
- It avoids unnecessary re-renders — which, in large apps, becomes critical.
Of course, it’s not about replacing React. But it opens the door to thinking differently about state.
3. Signals vs React State: A Developer's Viewpoint
Now let’s talk experience — the good, the not-so-good, and the trade-offs.
What I Loved About Signals
- No unnecessary re-renders. Since signals update only where they're used, the rest of the component tree stays untouched.
- Freedom from hook rules. You’re not restricted by the "rules of hooks" (no conditionals, top-level only, etc.).
-
Composable logic. Signals can be derived, composed, or reused without
useEffect
.
For instance:
const price = signal(100);
const quantity = signal(2);
const total = computed(() => price.value * quantity.value);
No useMemo
, no tracking dependencies. It just works.
What React Still Does Better
As much as I appreciated the elegance of Signals, I wouldn’t rush to replace React’s state system entirely.
- Ecosystem support. React has years of tools, libraries, and community patterns.
- DevTools integration. Debugging with React is well-supported. Signals still lack that full ecosystem visibility.
- Predictability in teams. React’s state model — though verbose — is familiar to most developers. Signals introduce a different mindset.
What I Struggled With
- Mindset shift. I had to unlearn the habit of thinking in components + hooks and start thinking in terms of reactive values and computations.
- TypeScript support. Early versions didn’t always provide perfect autocompletion or type inference. This has improved over time, but it’s something to watch.
- Team-wide adoption. If you're working in a team, especially one that's deep into React, introducing signals might create friction or confusion.
You do realize that preact "signals" just uses a useEffect and a useState hook under the hood right? You can't trigger a react component to rerender without changing its props, or using those hooks. There are no signals in React.