Skip to content

fix(react): stabilize streaming renders - #325

Merged
Railly merged 3 commits into
mainfrom
railly/fix-311-streaming-oom
Aug 30, 2026
Merged

fix(react): stabilize streaming renders#325
Railly merged 3 commits into
mainfrom
railly/fix-311-streaming-oom

Conversation

@Railly

@Railly Railly commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #311.

Append-paced streaming rebuilt the complete spec for every patch. Because each ElementRenderer received the new spec reference, unchanged elements still executed on every patch. Fresh bindings and resolved-props wrappers could then turn consumer effects into a Maximum update depth exceeded loop.

This change:

  • derives iterative bottom-up versions for elements and their structural descendants
  • skips unchanged internal element renderers before their hooks execute
  • memoizes catalog component execution across unchanged observable inputs
  • stabilizes resolved props and bindings with key-aware shallow equality
  • resets an element error boundary when a corrective streamed version arrives
  • keeps the validation-functions default stable

Public spec, streaming, renderer, and binding contracts remain unchanged.

Evidence

On a 26-element workload with 200 separately committed patches touching one element:

  • element/catalog executions: 5,226 before, 226 after
  • untouched leaf executions: 5,025 before, 0 after
  • binding-write reproduction: update-depth loop before, 26 bounded writes after

The regression matrix covers direct complete specs, later children, state updates, repeat callback freshness, functions and directives, shared-child DAGs, cyclic graphs, 12,000-node depth, watch cleanup after unmount, error recovery, cyclic runtime state, and BigInt values.

Verification

  • pnpm test: 1,085/1,085
  • pnpm type-check: 59/59
  • pnpm lint: 14/14
  • focused regression: 16/16, repeated five times
  • force-red mutations for renderer bailout, key ownership, bindings, callbacks, DAG invalidation, deep traversal, watch cleanup, and recovery
  • independent Gemini 3.7 Flash review: final PASS
  • Review Gate on exact signed SHA 476e27a: pass

Remaining gap

Chrome heap retention and Fiber collection after forced GC were not measured locally. The tests directly prove removal of the repeated React work and update loop, but do not claim a browser heap profile.

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
json-render Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-chat-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-dashboard-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-game-engine-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-image-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-no-ai Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-react-email-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-react-pdf-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-react-three-fiber-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-remotion-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-solid-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-svelte-chat-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-svelte-demo Ready Ready Preview, v0 Aug 28, 2026 7:46pm
json-render-vue Ready Ready Preview, v0 Aug 28, 2026 7:46pm

Comment thread packages/react/src/renderer.tsx Outdated
Comment thread packages/react/src/renderer.tsx Outdated
Comment thread packages/react/src/renderer.tsx Outdated
@Railly
Railly merged commit ea33260 into main Aug 30, 2026
23 checks passed
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.

[Bug] "Maximum update depth exceeded" from React when streaming many components eventually causing an out-of-memory crash

2 participants