Open-Source Wikis

/

Solid

/

Packages

/

solid-js/web (DOM renderer)

solidjs/solid

solid-js/web (DOM renderer)

Active contributors: Ryan Carniato, Damian Tarnawski

solid-js/web is the renderer that turns Solid components into real DOM. It is the module that the JSX compiler emits calls into, so almost every Solid app imports it (directly or indirectly) for render, hydrate, Portal, and Dynamic. Most of the low-level surface (insert, spread, template, delegateEvents, …) is re-exported from the sibling dom-expressions package and not implemented here.

Purpose

The browser entry of solid-js/web (packages/solid/web/src/index.ts) provides:

  • Bundled re-exports of dom-expressions's client runtime. This is what the compiler actually calls.
  • hydrate(...) — a wrapper around the dom-expressions hydrate that flips the enableHydration() switch in the reactive runtime first.
  • <Portal> — render outside the current parent (modals, tooltips, document.head injection).
  • <Dynamic> / createDynamic — render a component or HTML element decided at runtime.
  • Re-exports of control-flow tags (For, Show, Suspense, SuspenseList, Switch, Match, Index, ErrorBoundary, mergeProps) so the compiler's moduleName: "solid-js/web" setting can resolve everything from one place.
  • isServer and isDev booleans that flip based on which build the export conditions resolve to.
  • server-mock.ts shims for renderToString, renderToStringAsync, renderToStream, ssr, ssrElement, etc., that error helpfully if called from a browser-condition build.

The server entry (packages/solid/web/server/index.ts) is documented separately in solid-js/web server entry.

Directory layout

packages/solid/web/
├── package.json                 # ./web export selector
├── README.md
├── tsconfig.build.json
├── tsconfig.json
├── src/                         # browser entry
│   ├── index.ts                 # public surface: render, hydrate, Portal, Dynamic
│   ├── client.ts                # `export * from "dom-expressions/src/client.js"`
│   ├── core.ts                  # rxcore for browser; re-exports from solid-js
│   ├── jsx.ts                   # type re-export
│   └── server-mock.ts           # SSR-API stubs that error in the browser
├── server/
│   └── index.ts                 # SSR entry (Dynamic, Portal stub, mergeProps override)
├── storage/                     # solid-js/web/storage entry (own page)
└── test/                        # vitest suites for hydration, suspense, transitions, portals

The client.ts file is one line: export * from "dom-expressions/src/client.js". Everything else in src/index.ts either layers Solid-specific behaviour on top, or re-exports from solid-js.

Key abstractions

Symbol File Description
render(code, element, init?) re-export from dom-expressions Mounts a component into a DOM element. Returns a dispose function.
hydrate(code, element, options?) packages/solid/web/src/index.ts Like render, but reuses the existing DOM and attaches reactive subscriptions. Calls enableHydration() before delegating to dom-expressions.
Portal<T, S> packages/solid/web/src/index.ts Renders children into a mount Node (default document.body), wrapped in a <div> (or <g> if isSVG), optionally inside a Shadow DOM root. Cleans up on disposal.
Dynamic<T> packages/solid/web/src/index.ts Renders a component or string element decided at render time.
createDynamic<T>(component, props) packages/solid/web/src/index.ts Lower-level form of Dynamic. Used by libraries that already have an accessor for the component identity.
isServer packages/solid/web/src/index.ts false in browser builds, true in server builds.
isDev packages/solid/web/src/index.ts "_SOLID_DEV_" placeholder, replaced at build time.
insert, spread, template, delegateEvents, effect, memo, style, classList, setAttribute, dynamicProperty, mergeProps, getNextElement, Aliases, Properties, ChildProperties, DelegatedEvents, SVGElements, SVGNamespace re-exports from dom-expressions via client.ts The compiler-targeted runtime primitives.
For, Show, Suspense, SuspenseList, Switch, Match, Index, ErrorBoundary re-exports from solid-js Control flow available from the same module name as the runtime primitives.

How it works

graph TD
    Compiler["JSX → babel-preset-solid<br>moduleName: 'solid-js/web'"]
    SrcIndex["packages/solid/web/src/index.ts"]
    Client["packages/solid/web/src/client.ts<br>(re-exports dom-expressions/client)"]
    DomExp["dom-expressions<br>(workspace devDep)"]
    Runtime["packages/solid/src<br>reactive runtime"]
    ServerMock["server-mock.ts<br>SSR stubs that error"]

    Compiler -->|insert / spread / template / etc.| SrcIndex
    SrcIndex --> Client
    Client --> DomExp
    SrcIndex -->|hydrate, Portal, Dynamic| Runtime
    SrcIndex -->|browser condition| ServerMock

render and hydrate

render is dom-expressions's; Solid does not re-implement it. Internally it allocates a fresh Owner via createRoot, runs the user component, calls insert to mount the produced fragment, and returns a disposer.

hydrate exists in two places:

  • The dom-expressions implementation (hydrateCore here) takes the existing DOM tree and attaches reactive subscriptions in lockstep with the compiler-emitted template markers.
  • Solid's wrapper at the top of packages/solid/web/src/index.ts calls enableHydration() first. That flag (in packages/solid/src/render/component.ts) makes createComponent advance the sharedConfig.context.id counter so subsequent component invocations match the SSR-emitted ids.
export const hydrate: typeof hydrateCore = (...args) => {
  enableHydration();
  return hydrateCore(...args);
};

Portal

Portal allocates a marker text node, then in a createEffect mounts an internal container (<div> or <g>) into the chosen parent and inserts the children into it. Special cases:

  • mount defaults to document.body.
  • If mount is a <head>, the portal renders directly into the head (no extra container).
  • useShadow: true attaches a shadow root and renders inside it for style isolation.
  • isSVG: true switches to an SVG <g> container.
  • The container's _$host getter returns marker.parentNode so descendant code that walks back up the tree sees the original logical parent.
  • On cleanup, the container is removed from mount.

Dynamic and createDynamic

Dynamic is a thin wrapper that splits off the component prop and forwards the rest to createDynamic. The implementation creates a createMemo of component(), then in a nested memo dispatches:

  • typeof component === "function" → call it as a component (with dev-component branding in the dev build).
  • typeof component === "string" → either claim the next hydration node or createElement it, then spread the remaining props.

Both reactively re-render when component changes; switching a Dynamic from 'input' to 'textarea' swaps the underlying element while preserving sibling order.

The server-mock.ts shim

server-mock.ts is loaded only by the browser bundle (it is export * from "./server-mock.js" in src/index.ts). It defines stub bodies for every SSR API:

export function renderToString<T>(fn: () => T, options?): string {
  throwInBrowser(renderToString);
}

throwInBrowser calls console.error with a clear message. The @deprecated pipeToWritable and pipeToNodeWritable are also stubbed here.

This means: if your build accidentally pulls the browser entry of solid-js/web into Node, renderToString will not silently return undefined — you will see a <funcName> is not supported in the browser error in the console.

Key source files

File Purpose
packages/solid/web/src/index.ts Browser entry — hydrate, Portal, Dynamic, createDynamic, control-flow re-exports, isServer, isDev.
packages/solid/web/src/client.ts One-line re-export of dom-expressions's client runtime.
packages/solid/web/src/core.ts rxcore shim for the browser; exports getOwner, createComponent, createRoot as root, createRenderEffect as effect, untrack, mergeProps, sharedConfig, and memo. The Rollup config rewrites import "rxcore" to this file at build time.
packages/solid/web/src/server-mock.ts Browser-time stubs for SSR APIs, all marked @ts-nocheck.
packages/solid/web/server/index.ts Server entry. See solid-js/web server entry.
packages/solid/web/package.json The ./web exports selector with browser/dev/server/worker conditions.

Integration points

  • The compiler. packages/babel-preset-solid/index.js defaults moduleName: "solid-js/web", meaning every JSX expression turns into named imports from this module. See babel-preset-solid.
  • The reactive runtime. Everything solid-js/web does is built on top of solid-js's primitives — createSignal, createMemo, createEffect, createRoot, getOwner, runWithOwner, sharedConfig, enableHydration. See Reactivity primitives.
  • dom-expressions. The bulk of the renderer is in this sibling library, declared as a workspace devDependency in the root package.json. The client.ts re-export and the rxcore rewrite (packages/solid/rollup.config.js) are the connection points.
  • solid-element. Wraps Solid components as Web Components and uses insert (from solid-js/web) plus createRoot to mount them. See solid-element.
  • solid-js/h, solid-js/html. Both consume spread, assign, insert, createComponent, etc. from solid-js/web. See solid-js/h and solid-js/html.

Entry points for modification

  • Adding a new DOM-only component (e.g. <HeadPortal>): export it from packages/solid/web/src/index.ts, mirror it (likely as a stub) in packages/solid/web/server/index.ts, and add tests under packages/solid/web/test/.
  • Adjusting hydration: the hydrate wrapper in packages/solid/web/src/index.ts and enableHydration in packages/solid/src/render/component.ts. The hydration protocol (id counters, marker comments) is governed by dom-expressions and sharedConfig (packages/solid/src/render/hydration.ts), so coordinated changes are usually required.
  • Updating the dom-expressions integration: the workspace dom-expressions version is bumped together with babel-plugin-jsx-dom-expressions. Look for commits like Update dom-expressions packages to 0.40.6 for the cadence.

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

solid-js/web (DOM renderer) – Solid wiki | Factory