SolidJS — the ultimate review
Why is it still React, not Solid.JS?
In this article, we'll figure out what Solid.js is — a revolution, a worthy successor to React, or just another framework that leans on familiar APIs to lure React's dependent audience.
Solid boasts about its performance, purity, and reactivity. But dig a little deeper, and you see... React. Yes, the same hooks. The same component structure. The same habits.
Solid stands on the shoulders of giants. In its documentation, it references React and Knockout. If you've written in React with functional components and hooks before — you'll feel almost at home.
Solid doesn't invent new things. It optimizes the old. It takes familiar patterns and makes them faster, simpler, and more transparent. No virtual DOM. No component rerenders. No extra magic.
And here we get to the point: What's the real difference? Why did Solid even appear if "everything already works"?
Many people hate React's reconciliation and virtual DOM.
Cluck cluck cluck, it's slow. Cluck cluck cluck.
Let's remember why this was in React. It was created to simplify working with the real DOM. Here's the idea:
- The DOM in the browser is slow and fragile.
- It's inconvenient for developers to manage nodes, diffs, and attributes directly.
- Instead, React offers: just write how the UI should look if you have such-and-such data.
- And React itself, under the hood, will compare the old tree with the new one and carefully update only the necessary parts (this is reconciliation).
And then Solid comes along, and I have a dependency graph based on signals, I'll update only those parts of the app that really depend on the signals.
Solid doesn't recreate components or build a new virtual DOM. It updates only those DOM parts that actually depend on the changed state.
Conditional rendering looks like this:
<Show when={loggedIn()} fallback={<p>Not logged in</p>}>
<p>Welcome back!</p>
</Show>
Or like this:
const output = createMemo(() => {
return condition() ? <A /> : <B />;
});
But this won't work:
const [count, setCount] = createSignal(0);
const even = count() % 2 === 0;
return (
<>
<button onClick={() => setCount(count() + 1)}>Inc</button>
{even ? <p>Even</p> : <p>Odd</p>}
</>
);
even
is calculated once at initialization, because count()
is called outside JSX.
Solid doesn't track count
as a dependency for this expression.
Changing count()
doesn't cause even
to be recalculated, because even
is not reactive.
If you want to use conditional rendering inside the function body, do this:
return <>{count() % 2 === 0 ? <Even /> : <Odd />}</>;
I remember losing reactivity with proxy objects too. For example, in mobX, you can also lose reactivity if you extract a value from a reactive object at an irregular time — outside a reactive context.
const state = reactive({ count: 0 });
const even = state.count % 2 === 0;
watchEffect(() => {
console.log(even); // ❌ won't work, even is not reactive
});
We've found that loss of reactivity is possible with both proxies and signals.
So why not Proxy? Why do I need signals? What is a Signal anyway? I'm so lazy... I'll just leave here a code snippet of the readSignal
function.
export function readSignal(this: SignalState<any> | Memo<any>) {
const runningTransition = Transition && Transition.running;
if (
(this as Memo<any>).sources &&
(runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state)
) {
if ((runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state) === STALE)
updateComputation(this as Memo<any>);
else {
const updates = Updates;
Updates = null;
runUpdates(() => lookUpstream(this as Memo<any>), false);
Updates = updates;
}
}
if (Listener) {
const sSlot = this.observers ? this.observers.length : 0;
if (!Listener.sources) {
Listener.sources = [this];
Listener.sourceSlots = [sSlot];
} else {
Listener.sources.push(this);
Listener.sourceSlots!.push(sSlot);
}
if (!this.observers) {
this.observers = [Listener];
this.observerSlots = [Listener.sources.length - 1];
} else {
this.observers.push(Listener);
this.observerSlots!.push(Listener.sources.length - 1);
}
}
if (runningTransition && Transition!.sources.has(this)) return this.tValue;
return this.value;
}
The main magic is in the list of observers — a component or effect becomes a subscriber.
Can you mess up using this? Just like we saw above (in the example with loss of context).
Yes, you can.
Especially when your target audience is an army of React developers.
In React, everything was done to simplify and lower the entry threshold as much as possible. Just remember how hooks were sold when they first appeared. And functional components. At first, they sold them to us with the motivation: is it hard for you with class components? We've made it even easier.
Eh, maybe all these are far-fetched examples.
The most obvious thing, of course, is that if you use solid instead of react, you need much more enthusiasm. After all, there may not be an analogue of this or that package that exists for react. You'll have to create it yourself. Do you need it? Decide for yourself. Maybe everything will change tomorrow.
There should be a joke about PHP here.
Read next
- May 28, 2025
Poop Button
How to make a button that looks like a poop emoji on hover using only pure CSS — no SVG or images.
- May 13, 2025
Cloning My Voice with AI – What Happened to Theo Von?
I lost access to the Theo Von AI voice on Play.ht. This is the story of trying to clone my own voice with OpenVoice — and what went wrong