SolidJS Core: The React-Shaped Framework That Disappears at Runtime

A deep dive into how Solid keeps JSX and components, swaps out the virtual DOM, and updates the real DOM through fine-grained reactivity.

10 min read • View on GitHub • More from solidjs

A familiar component tree on the left dissolves into direct wires on the right, where signals connect straight to DOM nodes. The image explains Solid’s central trick: the framework surface stays familiar while the runtime scaffolding fades away.
Solid keeps the React-shaped surface, then routes updates through a much thinner execution path.
Key Takeaways

Solid is easiest to understand as a trick of subtraction. It gives you JSX, components, and a familiar way to structure UI, then quietly removes the part most frameworks spend their time on: repeated tree rerenders and virtual DOM diffing.

That is why the project is so interesting. It does not ask you to learn a totally new syntax. It asks you to notice that the syntax you already know can be compiled into a very different execution model.

The Framework That Compiles Its Own Shadow

Solid’s core move is simple to say and hard to internalize. Components are setup functions, not rerender functions. They run once, establish subscriptions, and get out of the way while the reactive graph takes over.

This diagram shows why Solid feels familiar at the surface but behaves differently underneath. One initial execution builds a graph, then later changes travel only through the nodes that care.

Why Components Run Once

The implementation in `createComponent` is the giveaway. Solid executes the component under `untrack`, which means the component is used to wire up reactive dependencies, not to serve as the recurring unit of rendering.

function createComponent(Comp, props) {
  return untrack(() => Comp(props));
}

const [count, setCount] = createSignal(0);

function Counter() {
  return <button onClick={() => setCount(count() + 1)}>{count()}</button>;
}

That code is not doing the thing React developers expect. The `Counter` function does not rerun every time `count` changes. Instead, reading `count()` inside the JSX creates a dependency that points directly at the text node that needs to change.

This is the first mental shift Solid asks for. A component is not a repaint boundary. It is a subscription factory.

It’s the process of running the components on the client for the first time in the browser to get them set up for future updates. We don’t render anything but it generally follows the same top-down execution.

That quote captures the oddness well. Solid still feels top-down when you read the code, but the runtime effect is bottom-up precision.

Signals, Dependencies, and the Tiny Graph Underneath

The engine lives in the reactive core, where signals act as producers and computations act as consumers. When a signal is read, Solid records the current listener. When that signal changes, it marks the relevant observers stale, then reruns only what is needed.

The important detail is not that this is reactive. Lots of systems are reactive. The important detail is that the graph is fine-grained enough to route a single update to a single leaf node without waking up an entire component tree.

A close-up machine diagram of a signal node pushing an update through a narrow chain of dependent computations to a single leaf DOM node. The image explains how Solid turns state changes into precise invalidation and patching instead of broad rerenders.
Solid’s signal graph is small, but that is the point. Small dependencies make small updates.

That is why Solid can feel almost invisible when it is doing its job. You notice the DOM update, not the framework logic around it.

The List Problem Is Where Frameworks Reveal Themselves

Lists force a framework to prove its claims. A button counter is easy. Inserts, deletes, and moves are where the update model stops being theoretical.

Solid’s `mapArray` logic and the `` component are the practical stress test. They preserve identity where it matters, reuse DOM where possible, and avoid the blanket churn that a broad rerender would create.

QuestionReactSolid
What reruns?The component function reruns.Only the dependent computation reruns.
How updates flowThrough reconciliation and a virtual DOM.Through direct subscriptions to DOM-relevant effects.
What tracks dependencies?Hooks and rerender boundaries.Signals and computations.
What is the runtime job?Compare trees and patch the result.Patch exact nodes that changed.
How lists behave?Reconcile against a rendered tree.Reuse and move only the affected items.

This is where Solid stops looking like a niche performance experiment and starts looking like a serious rendering system. It can still handle the hard parts, but it does so with a different topology.

The Compiler Is the Secret Sauce

Solid’s Babel preset is what turns the idea into machinery. JSX is not just syntax sugar here. It becomes instructions that wire templates, signals, and insertions directly to the real DOM.

// Conceptual output, simplified
const _tmpl$ = template(`<button></button>`);

function Counter() {
  const el = _tmpl$.cloneNode(true);
  insert(el, count);
  return el;
}

That is the hidden bargain. The compiler does more work up front so the runtime can do less forever after. The result is not merely a faster framework. It is a smaller runtime role for the framework itself.

A virtual DOM library asks JavaScript to keep comparing trees. Solid asks JavaScript to set up a graph once, then get out of the way.

Why Solid Looks Familiar to React Users and Still Isn’t React

FrameworkExecution modelUpdate mechanismMental model
SolidComponents run onceFine-grained signals to DOM nodesSetup code that builds subscriptions
ReactComponents rerun on state changeVirtual DOM diffing and reconciliationRender as a repeatable calculation
SvelteCompilation-firstGenerated DOM operationsCompile away the framework work
VueReactive primitives plus VDOMProxy-driven tracking and patchingReactive state feeding a renderer
AngularSignals plus framework orchestrationChange detection and signal-driven updatesFramework-managed application structure

Solid sits in a useful middle ground. It gives React users a familiar surface, borrows the compiled feel of Svelte, and speaks the same reactive language that Angular and Vue now emphasize. But its core bet remains distinct: the renderer should disappear into the graph.

That distinction matters because it changes where complexity lives. In Solid, the hard part moves from rerender orchestration into dependency precision.

What the Repo Tells You About the Project’s Priorities

The repository layout makes the philosophy hard to miss. The core reactive engine, browser bindings, store layer, SSR package, and Babel preset are separate packages in one monorepo. That is not accidental modularity. It is a map of the system’s priorities.

The TypeScript-heavy core, the benchmarking focus, and the tight coupling between compiler and runtime all point to the same thesis. Solid is not trying to be a generic UI toolkit with a small fast path. It is trying to make a different rendering model feel ordinary.

That is the real story. Solid does not just optimize the familiar. It changes what the familiar is doing in the first place.