solidjs/solid
solid-js/universal
Active contributors: Ryan Carniato
solid-js/universal is the entry point that lets Solid drive any tree-shaped target — terminal UIs, canvas frameworks, native mobile renderers, Figma plugin trees — instead of the DOM. It exposes a createRenderer factory; you supply the host operations (createElement, insertNode, getParentNode, etc.) and you get back a fully wired Solid renderer that the JSX compiler can target.
Purpose
solid-js/web is the renderer for one specific platform (the browser). solid-js/universal lets community projects build renderers for everything else without forking the framework. Combined with babel-preset-solid configured as generate: "universal" and moduleName: "<your-renderer>", the same JSX you write for a web app can be compiled to drive an arbitrary tree.
Directory layout
packages/solid/universal/
├── package.json # ./universal export selector
├── tsconfig.json
├── README.md # full example: building a custom DOM renderer
└── src/
├── index.ts # createRenderer wiring
└── universal.ts # type re-exports from dom-expressions/universalKey abstractions
| Symbol | Source | Description |
|---|---|---|
createRenderer<NodeType>(options) |
packages/solid/universal/src/index.ts |
Wraps dom-expressions's createRenderer, then attaches mergeProps from solid-js. Returns a Renderer<NodeType> object. |
RendererOptions<NodeType> |
re-exported from dom-expressions/universal |
The host-operation contract you must implement. |
Renderer<NodeType> |
re-exported from dom-expressions/universal |
What you re-export from your renderer module: render, effect, memo, createComponent, createElement, createTextNode, insertNode, insert, spread, setProp, mergeProps, use. |
How it works
import { mergeProps } from 'solid-js';
import {
createRenderer as createRendererDX,
type Renderer,
type RendererOptions,
} from './universal.js';
export function createRenderer<NodeType>(
options: RendererOptions<NodeType>
): Renderer<NodeType> {
const renderer = createRendererDX(options);
renderer.mergeProps = mergeProps;
return renderer;
}That is the entire implementation. dom-expressions/universal does the structural work — wiring up createComponent, effect, memo, createElement, insertNode, spread, etc. against your host operations. Solid's only contribution is to add its own mergeProps so consumers can rely on the standard prop-merging shape.
RendererOptions<NodeType> is the contract you need to satisfy:
{
createElement: (tag: string) => NodeType,
createTextNode: (value: string) => NodeType,
replaceText: (textNode: NodeType, value: string) => void,
setProperty: (node: NodeType, name: string, value: any, prev?: any) => void,
insertNode: (parent: NodeType, node: NodeType, anchor?: NodeType) => void,
isTextNode: (node: NodeType) => boolean,
removeNode: (parent: NodeType, node: NodeType) => void,
getParentNode: (node: NodeType) => NodeType | undefined,
getFirstChild: (node: NodeType) => NodeType | undefined,
getNextSibling: (node: NodeType) => NodeType | undefined,
}The README at packages/solid/universal/README.md walks through implementing a custom DOM renderer (with a PROPERTIES allowlist for className and textContent).
Connecting to the compiler
babel-preset-solid accepts moduleName and generate options. To target a custom renderer:
// .babelrc
{
"presets": [
[
"babel-preset-solid",
{
"moduleName": "solid-custom-dom",
"generate": "universal",
},
],
],
}The compiler then emits import { ... } from "solid-custom-dom" instead of solid-js/web. The names it imports (createComponent, createElement, effect, insert, spread, setProp, template, …) are exactly the ones returned by createRenderer. Re-export them from your renderer's index file.
graph LR
JSX[App JSX] -->|babel-preset-solid<br>generate: universal<br>moduleName: solid-custom-dom| Compiled[Compiled JS]
Compiled -->|imports from| Module[solid-custom-dom<br>your renderer module]
Module -->|createRenderer(...)| Universal[solid-js/universal]
Universal -->|wraps| DXUniv[dom-expressions/universal]
Universal -->|mergeProps| Solid[solid-js]
Module -->|"render(() => <App/>, root)"| Host[Your host system]Key source files
| File | Purpose |
|---|---|
packages/solid/universal/src/index.ts |
The 11-line wrapper that adds mergeProps to dom-expressions/universal's renderer. |
packages/solid/universal/src/universal.ts |
Type re-exports. |
packages/solid/universal/README.md |
Full example renderer with babel/vite configuration. |
Integration points
- Solid's
mergePropsis the only direct dependency onsolid-js;solid-jsisexternalin the Rollup config so consumers dedupe with their app'ssolid-js. dom-expressions/universalis a workspace devDependency (viadom-expressions) and provides the structural renderer.- The
babel-preset-solidgenerate: "universal"mode is what makes the JSX targetable. Seebabel-preset-solid.
Entry points for modification
- Changing the universal renderer surface: the function in
packages/solid/universal/src/index.tsis the entire surface. Most evolution happens indom-expressions/universal. - Adding new host operations as first-class: if a host operation should be supported by the compiler (e.g. a special
setStyle), it must land indom-expressionsfirst. Solid's wrapper here will pick up the new option automatically.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.