Open-Source Wikis

/

Solid

/

Solid

/

Architecture

solidjs/solid

Architecture

Solid is a fine-grained reactive UI library. Three layers cooperate to render a component: a compiler that rewrites JSX, a reactive runtime of signals, memos, and effects, and one or more renderers that turn the reactive graph into real output (DOM, HTML strings, custom backends).

High-level shape

graph LR
    JSX[JSX source<br>.tsx / .jsx]
    JSX -->|babel-preset-solid| Babel[babel-plugin-<br>jsx-dom-expressions]
    Babel -->|generates calls into| Runtime[Reactive runtime<br>packages/solid/src]
    Babel -->|generates calls into| Renderer[Renderer module<br>web / server / universal]
    Runtime <-->|graph subscriptions| Renderer
    Store[solid-js/store<br>proxy-based stores] -->|signals| Runtime
    Renderer -->|DOM nodes / HTML / custom| Output[Browser DOM<br>or HTML stream<br>or custom target]

babel-preset-solid (packages/babel-preset-solid/index.js) wires up babel-plugin-jsx-dom-expressions with default options: moduleName: "solid-js/web", the standard built-ins (For, Show, Switch, Match, Suspense, SuspenseList, Portal, Index, Dynamic, ErrorBoundary), and generate: "dom". The compiler emits direct calls into whichever module name you point it at — solid-js/web for browsers, solid-js/web (with the server export condition) for SSR, or any custom module for generate: "universal".

Reactive runtime

The runtime lives in packages/solid/src/reactive/:

  • signal.ts (~1800 lines) is the heart: it defines the Owner, Computation, SignalState graph and implements createSignal, createMemo, createEffect, createRoot, createResource, createContext, transitions, error boundaries, and the update queue.
  • scheduler.ts is a port of React's MessageChannel-based scheduler, used by enableScheduling() to time-slice work across frames.
  • array.ts provides mapArray and indexArray, the keyed/non-keyed list reconcilers consumed by <For> and <Index>.
  • observable.ts provides interop with TC39 Observables / RxJS via observable() and from().
graph TD
    Signal[SignalState<br>signal.ts] -->|notifies| Computation
    Computation[Computation<br>memo / effect] -->|reads| Signal
    Computation -->|owned by| Owner[Owner / context]
    Owner -->|registers| Cleanups[onCleanup]
    Updates[runUpdates queue] -->|drains| Computation
    Effects[runEffects queue] -->|user effects| Computation
    Transition[TransitionState] -.->|optional concurrent path| Updates
    Scheduler[scheduler.ts<br>MessageChannel] -.->|enableScheduling| Effects

A read of a signal inside a tracking scope (memo, effect, render function) registers the active Listener as an observer on the signal's observers array. A write enqueues observers for re-execution. batch, untrack, createRoot, and runWithOwner move the active Listener/Owner so that subscriptions are scoped correctly.

Render layers

packages/solid/src/render/ builds the component / control-flow / Suspense layer on top of the reactive runtime:

  • component.tscreateComponent, mergeProps, splitProps, the dev-component instrumentation ($DEVCOMP), and the ParentComponent / FlowComponent / VoidComponent types.
  • flow.ts — the <For> and <Index> control flows (the rest of <Show>, <Switch>, <Match>, <ErrorBoundary> live in signal.ts).
  • Suspense.ts<Suspense> and <SuspenseList>.
  • hydration.ts — the shared hydration context that lets the renderer pause/resume during streamed SSR.

The compiler emits calls into solid-js/web for DOM output. That module re-exports the bulk of its low-level primitives from dom-expressions (the runtime sibling library, declared as a workspace devDependency and aliased through the rxcore import — see packages/solid/web/src/core.ts). Solid's own additions on top of that are Portal, Dynamic, createDynamic, the hydrate enabler, and re-exports of For/Show/etc. from solid-js.

Server rendering

When the node, deno, or worker export condition is active, solid-js resolves to the server build (packages/solid/src/server/):

  • reactive.ts — a stripped-down reactive system that fakes createSignal, createMemo, createEffect to be synchronous and one-shot. Components run, their JSX resolves, and the result is serialized.
  • rendering.tscreateComponent, For, Show, Switch, Match, Suspense, ErrorBoundary, mergeProps, splitProps, createResource, lazy, plus the SSR helpers escape and resolveSSRNode.
  • solid-js/web server entry (packages/solid/web/server/index.ts) layers renderToString, renderToStringAsync, and renderToStream on top, using seroval to serialize promises, resources, and de-duplicate streamed data (see CHANGELOG 1.8.0).

The browser build of solid-js/web ships server-mock.ts (packages/solid/web/src/server-mock.ts) so that calls to renderToString from a browser-condition build error helpfully instead of silently returning empty strings.

Stores

packages/solid/store/ adds proxy-based reactive trees on top of solid-js:

  • store.ts defines createStore, unwrap, setProperty, and the proxy traps that lazily allocate per-property DataNode signals.
  • mutable.ts adds createMutable and modifyMutable, which apply mutations directly through proxy set traps wrapped in batch.
  • modifiers.ts provides produce (Immer-style) and reconcile (deep diff for setting whole subtrees while preserving identity).
  • server.ts is the server build that strips reactive subscription so setStore becomes a plain assign.

Alternative front ends

Two non-JSX entry points let Solid run without a compiler:

  • solid-js/h (packages/solid/h/src/index.ts) creates a HyperScript h() factory using createHyperScript from hyper-dom-expressions.
  • solid-js/html (packages/solid/html/src/index.ts) creates an html`` `` tagged template using createHTMLfromlit-dom-expressions`.

Both wire the dom-expressions runtime primitives (spread, assign, insert, createComponent, etc.) re-exported through solid-js/web.

Universal rendering

solid-js/universal (packages/solid/universal/src/index.ts) re-exports createRenderer from dom-expressions/universal. With babel-preset-solid configured as generate: "universal", the compiler emits calls into a renderer of your choice — terminal, native, canvas, etc. The package adds mergeProps from solid-js to the returned renderer object so it has the full Solid surface.

Custom elements

solid-element (packages/solid-element/src/index.ts) is a thin layer over the component-register library. customElement(tag, defaults, Component) registers a class that, on connect, calls createRoot, builds reactive props for each declared attribute via createSignal, and uses insert from solid-js/web to mount the component into the element's render root.

Build pipeline

turbo.json orchestrates per-package builds. packages/solid/rollup.config.js produces 16 bundles for the various entry points × runtime conditions (browser/dev/server) — the configuration is the single source of truth for which files turn into which dist/*.js. A babel-plugin-transform-rename-import step rewrites the special rxcore import inside Solid's source to the local web/src/core directory at build time, so that the same source code can produce both the browser DOM build (using dom-expressions browser core) and the server build (using a server core that knows how to escape HTML).

For a deeper walkthrough of the build orchestration and tooling, see Tooling.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – Solid wiki | Factory