[release/11.0-preview5] RenderFragment serialization#66754
Merged
wtgodbe merged 14 commits intoMay 20, 2026
Conversation
Co-authored-by: Copilot <copilot@github.com>
ilonatommy
approved these changes
May 20, 2026
lewing
approved these changes
May 20, 2026
Contributor
|
Hi @github-actions[bot]. This PR was just approved to be included in the upcoming servicing release. Somebody from the @dotnet/aspnet-build team will get it merged when the branches are open. Until then, please make sure all the CI checks pass and the PR is reviewed. |
wtgodbe
approved these changes
May 20, 2026
Member
|
Mac machines are down right now. Change was previously validated against |
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.
Backport of #66528 to release/11.0-preview5
/cc @dariatiurina
RenderFragment serialization
Summary
Enables non-generic
RenderFragmentparameters (e.g.,ChildContent) to cross interactive render mode boundaries (Server and WebAssembly). Previously, passing aRenderFragmentto a component with@rendermodethrew anInvalidOperationExceptionbecause delegates cannot be serialized. This PR introduces a serialization/deserialization mechanism that convertsRenderFragmentcontent into a JSON-serializable DTO during prerendering, allowing SSR-rendered content to be passed into interactive components.Lifecycle
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Parent as Parent (SSR) participant Boundary as SSRRenderModeBoundary participant Serializer as RenderFragmentSerializer participant Deserializer as Param Deserializer<br/>(Server / WASM) participant Child as Interactive Child Note over Parent,Child: ── Prerender ── Parent->>Boundary: params (incl. RenderFragment) Boundary->>Boundary: Wrap top-level RenderFragments Note right of Boundary: Wraps each RenderFragment<br/>with a RenderFragmentCapture decorator Boundary->>Boundary: Prerender (invoke wrapped fragments) Note right of Boundary: Capture decorator records<br/>produced RenderTreeFrames and<br/>recursively wraps nested fragments Boundary->>Serializer: SerializeFrames(captured frames) Serializer-->>Boundary: SerializedRenderFragment DTO Boundary-->>Parent: HTML + marker (JSON with DTO) Note over Parent,Child: ── Interactive activation ── Parent-->>Deserializer: marker arrives (Server circuit / WASM boot) Deserializer->>Serializer: Deserialize(DTO nodes) Serializer-->>Deserializer: live RenderFragment delegate Deserializer->>Child: activate with hydrated paramsBy centralizing the wrapping logic in
RenderFragmentCapture, the same infrastructure can be reused anywhereRenderFragmentserialization is needed (e.g.CacheBoundary).Serialization rules
tag+attrs+childrenKeyTypeName/KeyTypeAssemblycontentstringFullName+Assembly+ paramsKeyTypeName/KeyTypeAssemblyRenderFragment<T>TypeName/TypeAssemblyKey lifecycle phases
Validation —
SSRRenderModeBoundary.ValidateParametersnow allowsRenderFragmentdelegates through;RenderFragment<T>and other delegate types are still rejected.Capture — In
SetParametersAsync, each top-levelRenderFragmentparameter is wrapped with aRenderFragmentCapturedecorator and stored in a per-boundary_topLevelCapturesdictionary. When the wrapper is invoked during prerendering, it records the produced render tree frames into its own buffer. NestedRenderFragmentparameters on components inside a fragment are recursively wrapped viaRenderFragmentCapture.WrapNestedFragments, which uses the newRenderTreeBuilder.SetAttributeValueAPI to replace the delegate values in-place in the live render buffer.Serialize —
RenderFragmentSerializer.SerializeFrameswalks the captured frame span and converts it intoRenderTreeNodeDTOs. It preserves element/component keys withKeyTypeName/KeyTypeAssemblyfor correct round-trip through JSON, attribute values withTypeName/TypeAssembly, and component type names viaFullName+Assembly. NestedRenderFragmentparameters resolve via the parent capture'sChildCaptureslookup (keyed by attribute frame index). Non-serializable frames (event handlers,EventCallback,RenderFragment<T>, element/component ref captures, render modes, named events) are skipped with structured log warnings.Transport —
BuildSerializableParameterViewproduces a copy of the parameter dictionary in which eachRenderFragmentvalue is replaced with aSerializedRenderFragmentDTO. The DTOs travel inside the existing component marker JSON.Deserialize — Both Server (
ComponentParameterDeserializer) and WebAssembly (WebAssemblyComponentParameterDeserializer) detect when a parameter's type name matchesSerializedRenderFragment(in theMicrosoft.AspNetCore.Components.Endpointsassembly) and callRenderFragmentSerializer.Deserializeto reconstruct a liveRenderFragmentdelegate that replays the serialized nodes into the interactive component's render tree builder. Nested serialized fragments inside component parameters are recursively rehydrated.Changes
New
RenderFragmentSerializershared class (src/Components/Shared/src/RenderFragmentSerializer.cs): Core serialization/deserialization logic, plus supporting types:SerializedRenderFragment— DTO wrapper containingList<RenderTreeNode>RenderTreeNode/RenderTreeAttribute— JSON-serializable representation of the render treeRenderFragment<T>)New
RenderFragmentCaptureshared class (src/Components/Shared/src/RenderFragmentCapture.cs): Decorator that wraps a singleRenderFragmentdelegate, records the frames it produces when invoked, and recursively wraps any nestedRenderFragmentparameters it encounters on child components.New
RenderTreeBuilder.SetAttributeValuepublic API (RenderTreeBuilder.cs/PublicAPI.Unshipped.txt): Replaces the attribute value of an existing frame at a given index. Used byRenderFragmentCapture.WrapNestedFragmentsto swap nestedRenderFragmentdelegates with their capture wrappers in-place.SSRRenderModeBoundaryupdated:ValidateParameterspermitsRenderFragment(still rejectsRenderFragment<T>and other delegates)SetParametersAsyncwraps top-levelRenderFragmentparameters withRenderFragmentCaptureinstances stored in_topLevelCaptures(only whenPrerender=true)BuildSerializableParameterViewreplacesRenderFragmentvalues withSerializedRenderFragmentDTOs before marker serialization, throwingInvalidOperationExceptionif aRenderFragmentparameter is encountered without a corresponding capture (i.e., when prerendering is disabled)ILoggerforRenderFragmentSerializerviaHttpContext.RequestServicesServer
ComponentParameterDeserializerupdated: DetectsSerializedRenderFragmenttype name and deserializes to a liveRenderFragmentWebAssembly
WebAssemblyComponentParameterDeserializerupdated: Same detection/deserialization, with[DynamicDependency]attributes added forSerializedRenderFragment,RenderTreeNode, andRenderTreeAttributeto preserve them during trimmingShared sources linked into Endpoints, Server, and WebAssembly projects via
<Compile Include>directives in their.csprojfilesKnown limitations
RenderFragment<T>(templated content) cannot cross render mode boundaries — skipped during serialization with a warning log.RenderFragmentserialization requires the fragment to be invoked during prerendering to capture its frames. Attempting to serialize aRenderFragmentparameter when the render mode hasPrerender=falsethrowsInvalidOperationException.EventCallback,@refcaptures,@rendermodedirectives, and@formnamenamed events inside serialized fragments are dropped with structured warning logs; only structural markup and component declarations survive the boundary.Testing
RenderFragmentSerializerTest.cs, 26 test methods): Cover serialization of text/markup/elements/components/regions, attribute and key type round-tripping, skipping of delegate/event-callback/ref/render-mode/named-event/generic-fragment frames, nested fragment deserialization, and end-to-end JSON round-trips for complex trees and typed keys (string, int, Guid).SSRRenderModeBoundaryTestupdated: existing tests now use aCreateHttpContext()helper that registersILoggerFactory(needed becauseSSRRenderModeBoundarynow creates a logger forRenderFragmentSerializer).RenderFragmentSerializationTest.cs, 7[Theory]scenarios × Server + WebAssembly = 14 cases): Cover simple text, nested elements, mixed content, attributes, nestedRenderFragmentparameters, components with typed parameters, and keyed elements crossing the boundary. New test assets:RenderFragmentInteractive.razorpage, plusRenderFragmentChild.razorandTypedParameterDisplay.razortest components.Fixes #52768
Customer Impact
New feature to test in P5.
Regression?
Risk
New feature, no users will be impacted by changes.
Verification
Packaging changes reviewed?
When servicing release/2.3