solidjs/solid
babel-preset-solid
Active contributors: Ryan Carniato
Solid's compiler half. The published babel-preset-solid is a 35-line file that wires up babel-plugin-jsx-dom-expressions with Solid-friendly defaults so consumers can drop a single preset name into their Babel config and get the full JSX transform.
Purpose
Solid's runtime advantage — fine-grained reactive DOM updates without a virtual DOM — depends on the JSX you write being rewritten into direct calls into the runtime. babel-preset-solid is the published entry point that performs that rewrite, configured for Solid's specific runtime module names and built-in components.
The actual transform work happens inside babel-plugin-jsx-dom-expressions, declared as a runtime dependency of this preset.
Directory layout
packages/babel-preset-solid/
├── package.json # peerDependencies, runtime deps, test script
├── README.md # config recipes
├── CHANGELOG.md
├── index.js # the preset (35 lines)
└── test.js # runs babel.transformSync on a tiny snippetKey abstractions
| Symbol | Source | Description |
|---|---|---|
| Default export | packages/babel-preset-solid/index.js |
A function (context, options = {}) that returns { plugins: [[jsxTransform, mergedOptions]] }. |
Default options merged into babel-plugin-jsx-dom-expressions |
packages/babel-preset-solid/index.js |
moduleName: "solid-js/web", builtIns: ["For", "Show", "Switch", "Match", "Suspense", "SuspenseList", "Portal", "Index", "Dynamic", "ErrorBoundary"], contextToCustomElements: true, wrapConditionals: true, generate: "dom". |
How it works
const jsxTransform = require('babel-plugin-jsx-dom-expressions');
module.exports = function (context, options = {}) {
const plugins = [
[
jsxTransform,
Object.assign(
{
moduleName: 'solid-js/web',
builtIns: [
'For',
'Show',
'Switch',
'Match',
'Suspense',
'SuspenseList',
'Portal',
'Index',
'Dynamic',
'ErrorBoundary',
],
contextToCustomElements: true,
wrapConditionals: true,
generate: 'dom',
},
options
),
],
];
return { plugins };
};Every option is overridable. The most common reasons to override:
| Option | Effect | Typical value |
|---|---|---|
generate |
Selects compile target | "dom" (default), "ssr", "hydratable", "universal" |
hydratable |
Inserts hydration markers in the output | true for SSR + client builds that share data |
moduleName |
Where compiled code imports primitives from | "solid-js/web" (default) or your custom renderer module |
builtIns |
Names that get auto-imported instead of looked up by reference | Add "YourCustomFlow" if you ship one |
contextToCustomElements |
Walks Solid contexts through Custom Element shadow boundaries | true (default) |
The README at packages/babel-preset-solid/README.md shows the standard recipes:
// .babelrc — DOM
{ "presets": ["solid"] }
// SSR build
{ "presets": [["solid", { "generate": "ssr", "hydratable": true }]] }
// Client of an SSR app
{ "presets": [["solid", { "generate": "dom", "hydratable": true }]] }What the transform produces
packages/babel-preset-solid/test.js is a one-shot snapshot test that pins down the public output:
const { code } = babel.transformSync('const v = <div a b={2} />;', {
presets: [preset],
babelrc: false,
compact: true,
});
assert.equal(
code,
'import{template as _$template}from"solid-js/web";var _tmpl$=/*#__PURE__*/_$template(`<div a b=2>`);const v=_tmpl$();'
);Three things to notice in the output:
- JSX becomes a
_$templatecall carrying a static HTML fragment. - Imports come from
moduleName(solid-js/web). _$template(...)is annotated/*#__PURE__*/so bundlers can drop the call when its result is unused.
For non-trivial expressions, the transform also emits _$insert(_el$, () => ...) calls for reactive children, _$delegateEvents([...]) for event delegation, and _$spread(...) for spread attributes — all runtime helpers exported from solid-js/web.
graph LR
JSX[Source JSX] --> Babel[babel-plugin-jsx-dom-expressions]
Babel -->|template strings| Tmpl["_$template(...)"]
Babel -->|reactive bindings| Insert["_$insert(...)"]
Babel -->|spreads| Spread["_$spread(...)"]
Babel -->|delegated events| Delegate["_$delegateEvents(...)"]
Tmpl --> Web[solid-js/web]
Insert --> Web
Spread --> Web
Delegate --> WebKey source files
| File | Purpose |
|---|---|
packages/babel-preset-solid/index.js |
The full preset implementation. |
packages/babel-preset-solid/test.js |
The snapshot test. Run with pnpm --filter babel-preset-solid test. |
packages/babel-preset-solid/package.json |
Declares babel-plugin-jsx-dom-expressions as a dep and solid-js/@babel/core as peers. |
packages/babel-preset-solid/README.md |
Config recipes. |
Integration points
- The default
moduleName: "solid-js/web"is what makessolid-js/webthe canonical compile target. builtInsis the link between the preset and the components and control flow page. When a new flow tag is added tosolid-js, it should be added to this list so the compiler auto-imports it.- The plugin lives in the sibling
dom-expressionsrepository. Both Solid and the plugin are versioned together so that the runtime imports the plugin emits actually exist in the runtime version you ship. - Used by
vite-plugin-solid(workspace devDep),rollup-preset-solid(community), and thepackages/solid-ssrexamples (packages/solid-ssr/examples/*/rollup.config.js).
Entry points for modification
- Adding a new built-in: append to the
builtInsarray. Keep it in sync withpackages/solid/src/render/index.ts's public exports. - Bumping the plugin: update the
babel-plugin-jsx-dom-expressionsdependency. TheUpdate dom-expressions packages to ...commits ingit logshow the recurring cadence — the plugin and the runtime version insolid-jsare bumped together. - Changing default options: any change here is breaking for downstream consumers. Add a major-version changeset (
pnpm bump) and document the migration path in the changeset notes.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.