SolidJS is a declarative JavaScript library for building user interfaces that uses fine-grained reactivity to update the DOM directly.
It works without a Virtual DOM or a reconciliation pass, which sets it apart from most modern UI frameworks. Because it never diffs an entire component tree, a state change updates only the specific DOM nodes tied to that state.
That choice puts SolidJS closer to vanilla JavaScript than its competitors on most performance measures. Solid is a layer of reactive primitives with a deterministic execution model that maps your code directly to browser operations. You are trading a render cycle for a dependency graph.

What is SolidJS?
Ryan Carniato created SolidJS, and it has been in active development for nearly a decade. It uses JSX but compiles that syntax into direct DOM operations at build time rather than generating a virtual representation. The JSX will look familiar if you have written React, but the execution model underneath is completely different: it puts TypeScript first and keeps the runtime footprint small — roughly 7–7.6 KB.
Treat Solid as a fine-grained reactive layer rather than a monolithic framework. It handles the UI layer, while its companion SolidStart is the unopinionated orchestrator for full-stack work. The ecosystem is split between the stable 1.x line, which remains the standard for production, and the 2.0 release candidate (RC).
The current 1.x line has a proven API and mature documentation for stable deployments. Solid 2.0 is a coordinated release of the platform, including the core library, router, and meta-framework. During the RC phase, APIs can still change, and you have to keep the package versions inside that coordinated release compatible with each other.
The framework is designed to eliminate the render cycle entirely. Because components do not re-run during the application lifecycle, the library removes the non-deterministic behavior you get in frameworks that re-run functions repeatedly. Once setup is done, the reactive graph takes over and keeps the DOM in sync.
Why SolidJS doesn't need a virtual DOM
Solid and Virtual DOM (VDOM) frameworks like React differ mechanically in how they update. In a VDOM model, a state change re-runs component functions to generate a new virtual tree for reconciliation. Solid removes that middle layer by treating component functions as setup functions that run exactly once to build the reactive graph. It relies on a compiler that extracts static HTML into cloned template nodes, which are created once and then updated surgically.

The performance benefits show up in JS Framework Benchmark data. Solid's geometric mean overhead is 1.05x that of vanilla JavaScript, while React 19 shows a 1.42x overhead. The gap holds across individual operations: creating 1,000 rows costs 1.04x overhead in Solid versus 1.38x in React, and selecting a row is nearly native at 1.02x compared to React's 1.44x.
Memory allocation data tells the same story. Solid's memory overhead ratio is 1.26x relative to vanilla JS, whereas React 19 is more than double at 2.68x. In repeated create/clear cycles the gap widens, as React's memory usage can exceed 3x that of vanilla JS because the virtual tree structure persists.
Bundle size and first paint follow the same pattern. Solid ships with about 1.27x the overhead of vanilla JS for transferred size. React's runtime and VDOM requirements push its overhead to 12.76x. On low-end hardware or CPU-constrained mobile environments, that thinner runtime shows up directly as lower interaction latency and faster initial rendering.
Signals, effects and memos: the three reactive primitives
Solid's fine-grained reactivity rests on three core primitives: signals, effects, and memos. They are nodes in a reactive graph, and they propagate state changes synchronously.

- Signals are the primary data cells. They return a getter/setter tuple. The getter must be
called as a function,
count(), to create a subscription at runtime. - Effects are automated responders that observe signals. They execute a function whenever their tracked dependencies update.
- Memos are cached derivations that store the result of a computation, recalculating only when their source signals change. They are an optimization boundary that prevents redundant work.
import { createSignal, createEffect } from "solid-js";
// Initialize signal
const [count, setCount] = createSignal(0);
// Create an automated responder (Effect)
createEffect(() => {
// Accessing the getter count() here registers
// this effect as a subscriber for runtime tracking.
console.log("Current count:", count());
});
// Update the signal
setCount(5); // Synchronously triggers the effectSolid uses a hybrid push/pull approach to stay glitch-free, which makes it impossible to observe an
inconsistent state mid-update. Unlike React's useEffect, there are no manual dependency arrays.
Solid manages subscriptions automatically by tracking which signal getters run inside the
synchronous execution scope of an effect or memo.
Components run once: a different mental model from React
In Solid, components are factory functions, not render functions. They execute once to set up the reactive graph and DOM nodes, then effectively disappear from the execution stack. The JSX return statement tells Solid how to build the initial DOM and wire up the reactive bindings, but the function body is never called again.

That has one critical consequence — tracking is synchronous. Because Solid registers subscribers
only during the synchronous execution of a scope, calling a signal inside a setTimeout or another
asynchronous scope during setup does not register a subscription. You have to read every reactive
dependency inside the immediate, synchronous flow of the component or effect.
Standard JavaScript control flow, such as if statements or Array.prototype.map, does not work
reactively inside the JSX return statement, because that logic is only evaluated once during setup.
If the underlying data changes, the UI will not update. Solid provides built-in control flow
components that preserve fine-grained tracking:
<Show>handles conditional rendering.<For>is optimized for rendering lists with stable identities (keyed by reference).<Index>is optimized for lists where item values change but positions stay stable, minimizing DOM movement.<Switch>and<Match>manage complex, multi-conditional logic.
These components keep surgical updates scoped to the specific nodes affected by state changes without re-triggering parent logic. They are the main mechanism for fast list and conditional rendering without a Virtual DOM.
Common traps when you start with SolidJS
The most common error for developers moving to Solid is props destructuring. In Solid, the props
object is a reactive proxy. Reading a property like props.name inside a reactive scope creates a
subscription. If you destructure const { name } = props; at the top of a component, you read the
value once during setup and assign it to a local constant, permanently breaking the reactive link.

To manage component properties while keeping reactivity, use the utility functions Solid provides.
mergeProps lets you define default values while keeping the object reactive, and splitProps lets
you separate groups of props without destructuring the underlying proxy. Both keep the reactive
graph intact across component boundaries.
Another frequent error is confusing a signal call with a signal reference. You must call a signal as
a function — count() — to subscribe to its value. Referencing the signal without parentheses
(count) passes the function itself. Passing a signal reference is a valid technique for
composition or handing state to a child, but referencing it in a template without calling it
produces no reactive updates at that DOM node.
Finally, the run-once nature applies to the entire component body. Developers often expect code
inside the component, but outside of effects or JSX, to run again when props or state change.
Because the component is only a setup function, you have to wrap any logic that needs to respond to
changes explicitly in a createEffect, a createMemo, or a reactive JSX expression.
SolidStart: Solid as a full-stack framework
SolidStart is the meta-framework that extends Solid into a modular, full-stack orchestrator. It is built on Vite for bundling, Nitro for the server-agnostic web layer, and Vinxi as the underlying orchestrator. The architecture is deliberately modular and unopinionated, so you can swap individual components like the router when a project needs it.

Single-flight mutations are one of its bigger efficiency wins. Traditional frameworks often need two
separate HTTP requests — one for the data mutation and a second for state revalidation — while
SolidStart uses its router context to handle both in a single round trip. The "use server"
directive makes this work, letting you define functions that execute only on the server but stay
callable from client-side code.
The framework supports multiple rendering modes, including client-side rendering, server-side rendering, and static site generation. It uses streaming SSR to send HTML chunks to the browser as data resolves, which helps with slow APIs or edge environments. Streaming lets the browser start rendering the shell before the entire data payload is ready.
SolidStart also works well with headless CMS setups. Using the createAsync primitive, you can
fetch data from a CMS API inside a server function. This supports route preloading, where data
fetching runs in parallel with route rendering, so only the specific DOM nodes bound to that data
update when the resource resolves.
Where Solid 1.x and the 2.0 release candidate stand
The SolidJS ecosystem currently maintains two paths: the stable 1.x line and the 2.0 release candidate. Solid 1.x is the recommended choice for production applications, since it offers a hardened API and thorough documentation.

The 2.0 release candidate is a coordinated effort to modernize the core library, the router, and the meta-framework at the same time. It focuses on refining asynchronous primitives and improving how the platform's modules fit together. One major goal is a standard async layer that behaves consistently across server and client runtimes.
Specifically, the ecosystem is moving toward createAsync as the primary data-fetching primitive,
meant to replace createResource in route-related data loading. That fits the broader goal of 2.0:
a more unified async model for full-stack applications. While the RC phase is active, expect API
shifts as the core team finalizes the release.
Official migration guides will cover the move to 2.0 for anyone coming from Solid 1.x or earlier SolidStart releases. For now, keep 1.x for mission-critical production systems and use the 2.0 RC for greenfield exploration, or for projects where early access to the unified async primitives matters more than API stability.
When SolidJS is the right choice (and when it isn't)
SolidJS is a strong candidate for performance-critical UIs, data-intensive dashboards, and collaborative tools where interaction latency is the metric that matters. Its low-overhead runtime suits low-end hardware and bundle-sensitive environments where the memory and CPU costs of a Virtual DOM are unacceptable.

React remains the pragmatic choice when a project needs the large ecosystem of third-party component libraries — specialized data grids, complex date pickers — that have not been ported to Solid. And if hiring breadth and onboarding a large team quickly are your main risks, the ubiquity of React may outweigh the mechanical performance gains of Solid's fine-grained reactivity.
References
- Intro to reactivity — SolidJS Documentation
- Fine-grained reactivity — SolidJS Documentation
- Props — SolidJS Documentation
- Quick start — SolidJS Documentation
- Solid 2.0 Overview — SolidJS Documentation
- A Hands-on Introduction to Fine-Grained Reactivity — Ryan Carniato
- Solid.js Explained: Fine-Grained Reactive Framework — Strapi
- SolidStart: A Different Breed Of Meta-Framework — Smashing Magazine
- I Tried SolidJs in 2026 — Here Is What I Actually Think