feat: support component-local streaming boundaries - #460
Merged
Mohamed Mansour (mohamedmansour) merged 6 commits intoAug 23, 2026
Conversation
Replace fixed entry boundaries with runtime occurrences, resumable rendering, span-aware hydration, and start/resume/update APIs across all hosts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cache fragment records, reuse projection buffers, avoid redundant state overlays, remove plan locking, and reduce optional browser streaming code. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Clarify one-shot state snapshot reuse and record the final server and browser bundle measurements. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Make resume return boundary-only bytes, add advance for parent and tail bytes, and reject boundaries reached from repeat bodies. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot started reviewing on behalf of
Mohamed Mansour (mohamedmansour)
August 21, 2026 15:19
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds runtime-discovered, component-local streaming boundaries across the WebUI stack so hosts can flush and hydrate meaningful readiness points (including inside reusable components) without requiring sibling boundaries, via a unified start / resume / advance / update step machine.
Changes:
- Replaces compile-time boundary name tables with runtime-discovered boundary descriptors and a pull-style streaming step API across Node/WASM/Python/.NET/FFI/docs/examples.
- Introduces component span completion + ancestor-barrier bypass support in the framework streaming coordinator to enable child hydration ahead of unfinished parent tails.
- Evolves the protocol schema to inline boundary declarations (start/end tape) and adds build-time
contains_boundaryto avoid render-time graph walking.
Reviewed changes
Copilot reviewed 58 out of 132 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/webui/src/index.ts | Node host API: StreamingSession now returns StreamStep with optional BoundaryDescriptor. |
| packages/webui/README.md | Updates Node API documentation to the new step machine. |
| packages/webui-framework/src/template-element.ts | Adds ancestor-barrier activation status + bypass support for component spans. |
| packages/webui-framework/src/streaming-protocol.ts | Bumps browser streaming protocol to v2; adds span completion record + new bootstrap fields. |
| packages/webui-framework/src/streaming-dom.ts | Adds span marker support + shared marker resolution helpers. |
| packages/webui-framework/src/streaming-cleanup.ts | Extends failure cleanup to remove span markers/scaffolding and strip all streaming attrs. |
| examples/app/streaming/* | Refactors the demo to component-owned boundaries (<streaming-page>) and updates tests/docs. |
| examples/app/service-worker/* | Migrates WASM handler usage from callback streaming to host-driven streamResponse() steps. |
| dotnet/test/Microsoft.WebUI.Tests/* | Updates streaming fixtures/tests and enables multitargeting net8/net9. |
| crates/webui-protocol/proto/webui.proto | Schema update: inline boundaries + contains_boundary; removes name tables. |
| crates/webui-parser/src/plugin/webui.rs | Strips <boundary> directive tags from component metadata templates. |
| crates/webui-handler/* | Implements runtime-discovered streaming, component spans, and host-owned step APIs. |
| docs/guide/* | Syncs integration + CLI + concept docs to the new streaming contract. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…-streaming-boundaries # Conflicts: # DESIGN.md
Preserve literal boundary text, align updatable limits, remove dead span limits, avoid route capture allocations, and centralize activation outcomes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Mohamed Mansour (mohamedmansour)
requested review from
Bang Lee (Qusic),
Akrosh Gandhi (akroshg) and
mcritzjam
August 21, 2026 22:13
Jane Chu (janechu)
approved these changes
Aug 23, 2026
Mohamed Mansour (mohamedmansour)
merged commit Aug 23, 2026
35b2fdb
into
microsoft:main
24 checks passed
Mohamed Mansour (mohamedmansour)
deleted the
mohamedmansour-review-streaming-boundaries
branch
August 23, 2026 15:53
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.
Why
Applications should be able to keep component ownership intact while still choosing meaningful SSR readiness points. A page such as
<ntp-page>can remain the single entry shell for CSR, while an internal SearchBox becomes interactive as soon as its complete HTML is available instead of waiting for the entire page component and response tail.A boundary must also map to a real transport write. Committing SearchBox should return SearchBox and its checkpoint only; following parent or tail markup must not be bundled into that host write or require an artificial sibling boundary.
Streaming model
<boundary>is a compile-time directive authored in an entry or reusable component template. It emits no application DOM wrapper.A boundary-owning subtree may be reached through normal server-rendered selection:
<if>branch produces its occurrence; a false branch produces none<for>body cannot directly or transitively reach a boundaryA complete finite
<for>may be wrapped by one outer boundary and rendered as one atomic region. Boundaries before and after a repeat are also valid. This avoids retaining repeat iterators across host calls and keeps occurrence identity finite and predictable.At runtime, rendering discovers boundary occurrences in document order. Each occurrence has:
ownerandnameidentify the declaration.instanceIdidentifies one occurrence in one response.keydistinguishes a component-owned declaration reached from multiple static callsites.Host flow
Every host uses the same pull-style state machine:
A
StreamStepcontains emitted bytes, adoneflag, and the next optional descriptor:boundarypresent,done == falseresumefor that occurrencedone == falseadvancedone == truestartrenders the parent prefix and stops before the first boundary.resumerenders only the pending boundary through its checkpoint and returns immediately.advancerenders the following parent/shell bytes until the next boundary or terminal.This guarantees:
No sibling boundary is needed merely to force SearchBox into its own response write. An updatable occurrence may receive
updateafterresumeand beforeadvancewhile the response remains open.Rust, Node, npm, WASM, Python, C FFI, .NET, and the CLI control stream expose this same logical contract. The CLI automatically writes the boundary step and advance step separately while keeping backend controls at
start,resume, andupdate.Component-local hydration
When rendering suspends inside a reusable component, WebUI generates a temporary component span around the unfinished parent. A compiler-marked child boundary may hydrate through exactly that parent barrier while unrelated descendants remain deferred.
When the parent closes, a span-completion record hydrates the parent and remaining descendants exactly once. An already active child is not rehydrated and newer boundary updates are not overwritten. The same ownership rules apply in light DOM and declarative shadow DOM.
Parent continuation state is projected and frozen once. Boundary resume state overlays only newly resolved values, with resolution order:
Updatable occurrences retain only their successfully activated roots.
updateapplies a projected shallow state patch and never inserts markup or reruns hydration.Previous model vs current model
resumeis boundary-only;advanceowns following bytesAuthored boundaries cannot nest directly or transitively, and repeat bodies cannot reach them. Generated component spans are the only supported ancestor mechanism.
Performance and bounds
The continuation VM uses integer fragment slots, lock-free per-entry plans, persistent projection scratch, one frozen state snapshot per response, and no resumable repeat iterator. Valid browser commits use root-local marker ranges and never scan the document.
Measured release-mode server costs are:
The always-shipped framework entry is 60,528 bytes minified / 18,993 bytes gzip. Opting into streaming adds 16,652 bytes minified / 5,928 bytes gzip. Absolute ordinary and incremental bundle gates retain 4.2-4.7% headroom.
Continuation depth, occurrence count, static keys, open spans, retained roots, marker scans, records, and cleanup work are explicitly bounded. Malformed or truncated streams fail closed and release discoverable deferred state.
Validation
cargo xtask check