Skip to content

fix: render SSR-enabled build entries once by teeing the RSC stream#163

Merged
uhyo merged 1 commit into
masterfrom
claude/issue-147-i1elvc
Jul 21, 2026
Merged

fix: render SSR-enabled build entries once by teeing the RSC stream#163
uhyo merged 1 commit into
masterfrom
claude/issue-147-i1elvc

Conversation

@uhyo

@uhyo uhyo commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #147.

When ssr is enabled, build() in packages/static/src/rsc/entry.tsx rendered the identical element through two separate renderToReadableStream calls — one stream fed to renderHTML for SSR, the other written as the app RSC payload. As a result:

  • every server component executed twice per entry (double data fetching / build work);
  • every defer() call ran twice, registering duplicate registry entries whose payloads were both fully rendered (content hashing collapsed the duplicates to the same output file, hiding the waste but not avoiding it).

Change

Render the full tree once and tee() the resulting stream — one branch goes to renderHTML for SSR, the other becomes the app RSC payload. This halves build-time rendering per SSR entry and guarantees both consumers see byte-identical streams.

The tee()'d payload branch simply buffers while renderHTML drains the SSR branch; it is fully drained to a string shortly after in buildApp.ts, so there is no unbounded-buffering concern at build time. The non-SSR path is unchanged (its two streams render genuinely different trees). Dev-server rendering already used a single stream.

Testing

  • pnpm build and pnpm typecheck pass
  • pnpm test:run — 102 unit tests pass
  • pnpm test:e2e — all 31 e2e tests pass, including the ssr-defer suite which exercises SSR builds with defer()

🤖 Generated with Claude Code

https://claude.ai/code/session_01ShuMmxpPtcxZrHMQZdbq2a


Generated by Claude Code

When ssr is enabled, build() rendered the identical element through two
separate renderToReadableStream calls — one for SSR HTML and one for the
app RSC payload. Every server component (and defer() call) therefore
executed twice per entry. Render once and tee() the stream instead, so
both consumers see byte-identical streams with half the rendering work.

Closes #147

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ShuMmxpPtcxZrHMQZdbq2a
@uhyo
uhyo merged commit e5bc05b into master Jul 21, 2026
2 checks passed
@uhyo
uhyo deleted the claude/issue-147-i1elvc branch July 21, 2026 14:20
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.

SSR-enabled build renders each entry's tree twice

2 participants