Skip to content

fix: keep $ServerOnly templates in client HMR transforms - #362

Open
brenelz wants to merge 1 commit into
mainfrom
fix/server-only-templates-hmr
Open

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

Conversation

@brenelz

@brenelz brenelz commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #308.

The gap

babel-plugin-jsx-dom-expressions@0.40.10 added omitServerOnlyTemplates and defaults it to true. With hydratable: true, an element marked $ServerOnly then compiles to getNextElement() with no template argument. That is fine during hydration, but getNextElement falls back to template() whenever it is not hydrating, so re-creating the element on the client throws TypeError: template is not a function.

Solid Refresh does exactly that on every hot update: it disposes the old root and re-runs the component outside of hydration. So with solid({ ssr: true }) in dev, the first edit of any file containing a $ServerOnly element throws and leaves stale DOM (repro: https://github.com/birkskyum/repro-solid-server-only-hmr, downstream report solidjs/solid-start#1930).

Fix

The plugin already knows when HMR is active (needHmr) and whether a transform targets the client. For client transforms while HMR is active it now passes omitServerOnlyTemplates: false to babel-preset-solid, so the template stays registered and Solid Refresh can re-create the element. The hydration path is unchanged, since getNextElement still prefers the hydration registry.

  • Server transforms and production builds keep omitting the template, so no $ServerOnly markup ships to production clients.
  • An explicit solid.omitServerOnlyTemplates setting still takes precedence.
  • Older compiler versions that do not know the option ignore it.

Also adds omitServerOnlyTemplates to the Options['solid'] type, a README note, and a changeset.

Verification

Built the plugin and ran its transform hook on the repro's App.tsx against babel-preset-solid@1.9.15 / babel-plugin-jsx-dom-expressions@0.40.10:

Scenario Template registered
ssr: true, dev server, client transform yes, getNextElement(_tmpl$)
ssr: true, dev server, server transform n/a, ssr(...) output
ssr: true, dev server, hot: false no, getNextElement()
ssr: true, dev server, solid: { omitServerOnlyTemplates: true } no, getNextElement()
ssr: true, production build, client transform no, getNextElement()
solid: { hydratable: true } without ssr, dev server yes, getNextElement(_tmpl$)

tsc --emitDeclarationOnly and prettier --check pass on the changed files.

🤖 Generated with Claude Code

babel-plugin-jsx-dom-expressions 0.40.10 defaults `omitServerOnlyTemplates`
to true, so a `$ServerOnly` element compiles to `getNextElement()` with no
template. That only works during hydration; Solid Refresh re-runs the
component outside of hydration on every hot update, which then throws
`TypeError: template is not a function` and leaves stale DOM.

Default `omitServerOnlyTemplates` to false for client transforms while HMR
is active (dev server, non-production mode, `hot` not disabled). Server and
production transforms keep omitting the template, and an explicit
`solid.omitServerOnlyTemplates` setting still wins.

Fixes #308

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b50c064

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

This PR includes changesets to release 1 package
Name Type
vite-plugin-solid 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 Sep 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/vite-plugin-solid@362

commit: b50c064

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retain $ServerOnly templates while HMR is active

1 participant