Add start.renderMode ('stream' | 'async') with per-request module and handleRequest override - #349
Merged
Merged
Conversation
…d handleRequest override Streaming SSR leaves <Loading> fallbacks unresolved for clients that never run the swap scripts (solidjs/solid#3280). `start.renderMode: 'async'` makes the generated handler adopt the renderToStream thenable deliberately, so one settled document goes out: boundaries spliced in place pre-flush, no fallback markup or swap scripts, hydration data intact, and the string takes createSSRResponse's string path (stub commit, doctype/client-entry injection, mid-render Location as a real 3xx). A module path default-exporting (event) => 'stream' | 'async' decides per request (middleware/setup convention), and handleRequest(request, { renderMode }) overrides both; invalid values are rejected at config time and at dispatch with the source named. Generated entries commit the response head at render completion (onCompleteAll) so httpStatus/httpHeader survive the runtime's dispose-before-resolve on the awaited path. Fixes #344 Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 0985ce8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
commit: |
This was referenced Sep 5, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #344 (motivation: solidjs/solid#3280 — streaming SSR leaves
<Loading>fallbacks unresolved for clients that never run the swap scripts).What
start.renderMode?: 'stream' | 'async'(default'stream'), baked into the generated SSR handler. Under'async'the handler deliberately adopts therenderToStreamthenable (if (mode === 'async' && result && typeof result.then === 'function') result = await result) so one settled document goes out: boundary content spliced in place pre-flush, no fallback markup, no swap templates/scripts, hydration data still serialized. The string takescreateSSRResponse's existing string path (stub commit, doctype + client entry injection, mid-renderLocation→ real 3xx). Thepipe-before-thencheck is kept for stream mode.start.renderMode: './src/render-mode.ts'— module path default-exporting(event) => 'stream' | 'async' | Promise<...>, following themiddleware/setupconvention; resolved per request inside the request scope after the middleware chain.handleRequest(request, { renderMode })runtime override. Precedence: runtime option > module fn result > static config.render()returning arenderToStreamresult is awaited the same way). Client mode ignores the option.StartOptionsandhandleRequestoptions; README section covering what async does, tradeoffs (TTFB waits for full settle, whole page buffered),deferStreammoot under async, crawler-UA /?nojsper-request recipe, redirects becoming real 3xx.@solidjs/webchanges.Runtime note (deviation from the issue's design text)
On the awaited path
@solidjs/webdisposes the render owner (runningonCleanupforhttpStatus/httpHeader) beforecreateSSRResponsecommits the response stub, so under'async'a barehttpStatus(404)would come back as 200. Generated entries now passonCompleteAlltorenderToStream, committing the response stub at render completion so status/headers survive. Authored entries that want the same guarantee should do likewise (documented in the README). Redirects were unaffected either way.Tests
New
render-modemode inexamples/start-ssr/test/run.mjs:'async': complete document (doctype… in one piece, noLoadingmarkup, no swap templates/scripts, boundary content in place, hydration data present), then the existing hydration/RPC/browser checks against the async-rendered page;httpStatus(404)/httpHeader/ pre-flush and mid-renderLocation→ real 3xx with no bodyx-render-mode: async/ crawler UA /?nojssettle, and the same server streams again for the next plain requestFull gate (start-ssr incl. http-bridge, start-client, start-env, ssr, css-matrix, vitest/cypress on Vite 8) run locally — results in the PR conversation.
Made with Cursor