Skip to content

Adapter 0.9.0: pages stream, as on a Worker, with a /stream page that proves it - #61

Merged
nicu-chiciuc merged 4 commits into
mainfrom
nicu-stream-page
Oct 16, 2026
Merged

nicu-chiciuc merged 4 commits into
mainfrom
nicu-stream-page

Conversation

@nicu-chiciuc

@nicu-chiciuc nicu-chiciuc commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Closes issue 49.

Adapter 0.9.0: pages stream

The adapter's server entry is now TanStack's own stream handler, createStartHandler(defaultStreamHandler), plus the URLSearchParams.size getter Convex lacks. The whole-render wait, the renderSsrHtmlResponse detour, the doctype trimming, and the progressiveChunkSize override are gone.

The one obstacle was bundling, not the runtime. TanStack's stream renderer and router-core's stream transformer import Node's stream classes at the top of their files, for a branch that runs only where React has no Web Stream renderer. Convex has that renderer, so the branch never runs, and Convex's bundler had no node:stream to resolve. convexSsr() now resolves node:stream to a stub module shipped with the adapter, whose classes throw if anything calls them. node:async_hooks already bundled, Convex provides AsyncLocalStorage. The example's server bundle after the change imports node:async_hooks and nothing else from Node.

Every host TanStack documents has Node's stream module: Node, Bun, Deno, Netlify's functions, Vercel through Nitro, and Cloudflare Workers through nodejs_compat, on by default since compatibility date 2026-08-04. Convex's runtime is the one without it, so nobody upstream has needed the split; the one related PR, TanStack/router#8094 for Solid, has waited since August without review and does not touch the React file. The stub is independent of upstream.

/stream: the proof, in both examples

The loader returns a promise that resolves two seconds later, without awaiting it, and the page renders it with <Await> behind a fallback. The header link requests the document, because a navigation inside the app would run the loader in the browser. Fetched with a browser user agent:

Host First byte Last byte
Convex, before 2.4 s 2.4 s
Convex, after 0.5 s 2.5 s
Workers twin 0.1 s 2.1 s

With curl's own user agent TanStack takes the client for a bot and sends the whole document at 2.2 s, on both hosts. /, /about/, /todos (307 to /signin), /signin, and a 404 answer as before.

Also

The twin's nodejs_compat flag line is gone: Cloudflare turns it on by default from compatibility date 2026-08-04, the twin's is 2026-10-01, and the twin builds and runs without it.

Adapter 0.9.0 breaks nothing in apps: src/server.ts keeps re-exporting the entry; the three apps need only the dependency bump. The header fix is PR 63.

🤖 Generated with Claude Code

…wait

The same page in both examples. Its loader returns a promise that resolves two seconds later,
and the page renders it with Await behind a fallback. On a host that streams, the document goes
out at once with the fallback and the value follows in a later chunk, with React's swap script;
on a host that sends the document whole, everything arrives after two seconds. The Workers twin
under wrangler dev, fetched with a browser user agent: 4.2 kB at 0 ms, the rest 2.0 s later. With
curl's own user agent TanStack treats the client as a bot and waits for the whole document first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Preview of examples/tanstack-ssr: https://greedy-orca-541.convex.site

@github-actions

Copy link
Copy Markdown

Preview of examples/tanstack-spa: https://wooden-jaguar-615.convex.site

@pkg-pr-new

pkg-pr-new Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@samebase/convex-hosting@61
pnpm add https://pkg.pr.new/@samebase/convex-hosting-tanstack-start@61

commit: 4bb8c22

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://nicu-stream-page-convex-hosting-tanstack-ssr-workers.pfp.workers.dev (commit 4bb8c22)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://718ee663-convex-hosting-tanstack-ssr-workers.pfp.workers.dev 4bb8c22 2026-10-16T19:40:00.236Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://7f612cbd-convex-hosting-tanstack-ssr-workers.pfp.workers.dev 9d41bb0 2026-10-16T19:25:19.201Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://0c65ed3e-convex-hosting-tanstack-ssr-workers.pfp.workers.dev 1fa77a9 2026-10-16T19:29:18.885Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://d667217c-convex-hosting-tanstack-ssr-workers.pfp.workers.dev 7a33acc 2026-10-16T18:46:33.688Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://35bbec13-convex-hosting-tanstack-ssr-workers.pfp.workers.dev 89394d3 2026-10-16T18:00:19.809Z Visit the dashboard ↗

nicu-chiciuc and others added 2 commits October 16, 2026 21:42
…ate 2026-08-04

Cloudflare turns nodejs_compat on by default for Workers whose compatibility date is 2026-08-04
or later (changelog 2026-08-04-nodejs-compat-default); the twin's date is 2026-10-01. Checked by
building and running the twin without the flag under wrangler dev: the server bundle still imports
node:stream and node:async_hooks, and the Worker serves the pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codex: a navigation inside the app runs the page's loader in the browser, so the timer ran there
on both hosts and the demo never reached the server. The header link now reloads the document.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nicu-chiciuc nicu-chiciuc changed the title Examples: a /stream page that shows whether the host streams Examples: a /stream page, and a header that renders the session state on the server Oct 16, 2026
@nicu-chiciuc nicu-chiciuc changed the title Examples: a /stream page, and a header that renders the session state on the server Adapter 0.9.0: pages stream, as on a Worker; a /stream page that proves it; the header renders the session state on the server Oct 16, 2026
@nicu-chiciuc nicu-chiciuc changed the title Adapter 0.9.0: pages stream, as on a Worker; a /stream page that proves it; the header renders the session state on the server Examples: a /stream page, and a header that renders the session state on the server Oct 16, 2026
The server entry is TanStack's own stream handler: React's Web Stream renderer, the document sent
as it renders, each Suspense boundary and deferred loader value in a later chunk, status and
redirects decided before the first byte, the whole document for a client TanStack takes for a bot.
The whole-render wait and the progressiveChunkSize override of 0.6.0 are gone with it.

The one obstacle was bundling: TanStack's stream renderer and router-core's stream transformer
import Node's stream classes at the top of their files for a branch that runs only where React has
no Web Stream renderer. Convex has that renderer, so the branch never runs, and Convex's bundler
had no node:stream to resolve. convexSsr() now resolves node:stream to a stub whose classes throw
if anything calls them. node:async_hooks already bundled, Convex provides AsyncLocalStorage.

Measured on the SSR example's dev deployment, /stream with a browser user agent: first byte at
0.5 s with the fallback, last byte at 2.5 s with the value; with curl's user agent, the whole
document at 2.2 s. The Workers twin: 0.1 s and 2.1 s. Closes #49.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nicu-chiciuc nicu-chiciuc changed the title Examples: a /stream page, and a header that renders the session state on the server Adapter 0.9.0: pages stream, as on a Worker, with a /stream page that proves it Oct 16, 2026
@nicu-chiciuc
nicu-chiciuc merged commit 5e516c1 into main Oct 16, 2026
9 checks passed
@nicu-chiciuc
nicu-chiciuc deleted the nicu-stream-page branch October 16, 2026 19:58
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.

Stream server-rendered pages, as the Worker does

1 participant