Skip to content

feat: add ssr: "data-only" (1.x) - #2353

Merged
brenelz merged 1 commit into
1.xfrom
feat/ssr-data-only-1x
Oct 15, 2026
Merged

brenelz merged 1 commit into
1.xfrom
feat/ssr-data-only-1x

Conversation

@lxsmnsyc

Copy link
Copy Markdown
Member

For #2287 on the 1.x line. The same feature for 2.x is in #2352.

What this adds

export default defineConfig({ ssr: "data-only" });

The app renders on the client with render(), as with ssr: false, so there's no hydration and no app HTML from the server. The server still runs the route preloads for the requested page and streams their query results with the HTML shell. On the first render, the client's queries take those results instead of fetching them again.

How it works

The design is the same as #2352.

Server. The router's data-only pass collects the page's query promises. Single-flight mutations already use this pass in runtime/server-handler.ts. The SPA shell then renders with renderToStream, and each promise goes to sharedConfig.context.serialize under a <Hydration> boundary, which is Solid's own SSR serializer. <HydrationScript eventNames={[]}> defines _$HY.r.

Responses from a query. query skips its own Response handling in data-only mode, so it's applied here:

  • Headers are copied to the page response, except the body headers.
  • A redirect that's known before the shell flushes becomes a real redirect, and a thrown redirect isn't reported as an error.
  • A json() response sends its body.

Client. mount() points sharedConfig.has/sharedConfig.load at _$HY.r for the first render(). Router 0.15's query reads serialized data through them, the same as router 1.0.

Differences from 2.x

  • ssr: false prerenders / into a static shell. Data-only mode doesn't, because each request needs its own data.
  • The client Solid plugin received ssr: start.ssr, and the "data-only" string would have turned on hydratable output. It now gets start.ssr === true.
  • The SPA server entry now exports the deprecated getServerFunctionMeta, as the SSR entry does. decorateHandler and decorateMiddleware don't exist on 1.x.

Limits

These are the same as #2352:

  • Data needs a streamed render: nothing is sent with mode: "sync".
  • The root renders on the server during the data pass. If it throws there, the client fetches its data as before.
  • Query data uses Solid's serializer plugins, the same as SSR query data.

Verification

Production build of the basic fixture with a route whose preload calls four queries (data, json() with set-cookie, a throw, and a redirect on another route):

Check Result
Shell streams _$HY.r[...] for each query; Content-Type: text/html; set-cookie from the json() query
Client renders with the streamed data, Date and Map intact, error shown by the ErrorBoundary, 0 server function calls
Redirect route 302 with location: /co-b
Client navigation to the page fetches as usual (3 calls)
ssr: false still prerenders .output/public/index.html, no _$HY
  • packages/start: 106 unit tests (11 new), typecheck and build pass.
  • apps/tests: the unit test passes. Cypress: 18 of 19 pass.
  • The one Cypress failure is server-function.cy.ts › "should reject the promise when the response is a 5xx without an X-Error header". It fails the same way on clean 1.x.

🤖 Generated with Claude Code

The app renders on the client without hydration, as with `ssr: false`, but
the server runs the requested page's route preloads and streams their query
results with the HTML shell. The client's queries read those results on the
first render instead of fetching them again.

The server collects the queries with the router's data-only pass, the same
one single-flight mutations use, and streams them through Solid's own
serializer under a `<Hydration>` boundary. On the client, `mount()` points
`sharedConfig.has`/`load` at `_$HY.r` for the first `render()`, which is
where `query` already looks for serialized data.

Data-only mode doesn't prerender `/` the way `ssr: false` does, because each
request needs its own data, and the client is compiled without hydration
markers.

Also export `getServerFunctionMeta` from the SPA server entry, as the SSR
entry does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ef2391d

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 Minor

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 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: ef2391d

@lxsmnsyc lxsmnsyc mentioned this pull request Oct 14, 2026
2 tasks done
@brenelz
brenelz merged commit 0a027ee into 1.x Oct 15, 2026
7 checks passed
@brenelz
brenelz deleted the feat/ssr-data-only-1x branch October 15, 2026 20:09
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.

2 participants