Skip to content

fix(config): keep $ServerOnly templates in the client dev build - #2342

Closed
lxsmnsyc wants to merge 1 commit into
mainfrom
fix/server-only-hmr
Closed

lxsmnsyc wants to merge 1 commit into
mainfrom
fix/server-only-hmr

Conversation

@lxsmnsyc

Copy link
Copy Markdown
Member

Fixes #1930. The 1.x fix is #2341.

In dev, editing a component whose root element uses $ServerOnly throws TypeError: template is not a function on the first hot update, and the component's content disappears from the page.

Cause

In hydratable builds, the Solid compiler leaves the templates of $ServerOnly elements out of the client bundle. Hydration does not need them, since it reuses the server-rendered DOM. Hot module replacement re-runs the component on the client, though, and there is no template to create it from. birkskyum reduced this to a plain vite-plugin-solid reproduction in solidjs/solid-vite-plugin#308, which is still open.

Fix

babel-plugin-jsx-dom-expressions 0.40.10 added an omitServerOnlyTemplates option, and babel-preset-solid passes it through.

  • New config/solid.ts exports solidPlugins. It hands vite-plugin-solid its own solid options object and sets omitServerOnlyTemplates: false from configResolved, using the same condition vite-plugin-solid uses to enable hot reloading (command === "serve", mode !== "production", hot !== false). vite-plugin-solid reads these options on every transform, so the value applies to the whole dev session. The option only affects client (DOM) output, so SSR output is unchanged.
  • Production builds still leave the templates out. If the user sets omitServerOnlyTemplates themselves, their value is kept.
  • Lockfile: babel-preset-solid 1.9.12 → 1.9.15, which brings babel-plugin-jsx-dom-expressions 0.40.7 → 0.40.11. I picked 1.9.15 rather than 1.9.16 because 1.9.16 requires solid-js ^1.9.16 and the repo is on 1.9.15. Those two packages are the only lockfile changes.
  • Older compiler versions ignore the option, so nothing changes for them. Fresh installs already get 0.40.11.

Testing

  • New solid.spec.ts runs the real vite-plugin-solid transform through solidPlugins on a $ServerOnly component. It checks that the client dev build keeps the template, the production build leaves it out, a user value of true is kept, and hot: false leaves it out. The dev-build case fails without the fix.
  • basic fixture in a browser, route with a $ServerOnly root: before, the first edit threw template is not a function and the content disappeared. After, the edit applies with no errors. With the user setting omitServerOnlyTemplates: true, the error comes back, as expected.
  • Production build: the $ServerOnly text is in the server output and in none of the 7 client JS files. As a control, forcing omitServerOnlyTemplates: false everywhere puts it in a client file, so the check does detect a leak.
  • @solidjs/start: test:ci (217 tests), typecheck, validate-imports and build pass.
  • apps/tests: unit tests, Playwright e2e (42) and bundled-dev e2e (42) pass.

🤖 Generated with Claude Code

The Solid compiler leaves the templates of $ServerOnly elements out of
the client bundle, since hydration reuses the server-rendered DOM. Hot
module replacement re-runs the component on the client and needs them,
so the first edit to a component whose root uses $ServerOnly threw
"template is not a function".

- Add solidPlugins, which passes omitServerOnlyTemplates: false to
  vite-plugin-solid in dev, under the same condition vite-plugin-solid
  uses to enable hot reloading. Production builds still leave the
  templates out, and a value set by the user is kept.
- Update the lockfile to babel-preset-solid 1.9.15, which brings
  babel-plugin-jsx-dom-expressions 0.40.11. The option was added in
  0.40.10. 1.9.15 still accepts solid-js 1.9.15.

Fixes #1930.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@netlify

netlify Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for solid-start-landing-page ready!

Name Link
🔨 Latest commit 6179bd6
🔍 Latest deploy log https://app.netlify.com/projects/solid-start-landing-page/deploys/6acda0726ea929590681c439
😎 Deploy Preview https://deploy-preview-2342--solid-start-landing-page.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented Oct 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6179bd6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solidjs/start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/start@2342

commit: 6179bd6

@brenelz

brenelz commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

I had a potential fix for this here. Is this a more complete one?

solidjs/solid-vite-plugin#362

@lxsmnsyc

Copy link
Copy Markdown
Member Author

Closing in favor of solidjs/solid-vite-plugin#362, which makes the same fix in vite-plugin-solid itself and covers both SolidStart lines through their existing vite-plugin-solid ranges.

@lxsmnsyc lxsmnsyc closed this Oct 14, 2026
@lxsmnsyc

Copy link
Copy Markdown
Member Author

@brenelz I wasn't aware of it. It supercedes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug?]: dev hmr error: "template2 is not a function"

2 participants