Cannot pass the parameter 'ChildContent' to component 'Panel' with rendermode
'InteractiveServerRenderMode'. A statically rendered page handed a piece of code,
here the markup between the component's tags, to a child that runs interactively. Only
data can cross that boundary. Build the content inside an interactive component, or make
the parent interactive.
The same message appears for Action and Func parameters, with the
delegate type named at the end; for an Action OnSaved it reads
'System.Action'. An EventCallback does not throw at all, which
turns out to be worse.
The error
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Cannot pass the parameter 'ChildContent' to component 'Panel' with rendermode 'InteractiveServerRenderMode'. This is because the parameter is of the delegate type 'Microsoft.AspNetCore.Components.RenderFragment', which is arbitrary code and cannot be serialized.
at Microsoft.AspNetCore.Components.Endpoints.SSRRenderModeBoundary.ValidateParameters(IReadOnlyDictionary`2 latestParameters)
at Microsoft.AspNetCore.Components.Endpoints.SSRRenderModeBoundary.SetParametersAsync(ParameterView parameters)
The page that produced it has no render mode of its own:
@page "/delegate-child"
<h1>Panel</h1>
<Panel @rendermode="InteractiveServer">
<p>Next patient: David Chen</p>
</Panel>
Why it happens
A page without a render mode renders once on the server, and its component instance is
gone when the response ends. Putting @rendermode="InteractiveServer" on a
child starts a boundary: Blazor prerenders Panel, then writes a marker comment
into the HTML that carries the component type and its parameters, serialized and
protected, so the circuit can build a fresh Panel once the browser connects.
A string or a number survives that trip. Child content does not: the markup between the
tags compiles to a RenderFragment, a delegate pointing at code in a page
instance that no longer exists. Blazor checks the parameters at the boundary and refuses.
EventCallback is a struct, not a delegate, so it slips past that check. The
page rendered with a 200 and the button appeared, but clicking it did nothing: the child
logged OnSaved.HasDelegate = False and the page's handler never ran. No error,
just a dead button.
The fix
Move the content into a small component, NextPatientPanel.razor:
<Panel>
<p>Next patient: David Chen</p>
</Panel>
and give that component the render mode instead:
<NextPatientPanel @rendermode="InteractiveServer" />
The boundary moved up one level. Nothing crosses it now except the component type, and the
RenderFragment is created inside the interactive component, next to the
Panel that uses it. For callbacks, the simplest cure is to make the parent
interactive, so parent and child live in the same circuit:
@page "/delegate-param"
@rendermode InteractiveServer
<h1>Appointments</h1>
<SaveButton OnSaved="HandleSaved" />
<p id="status">@status</p>
Clicking now ran the handler and the page showed "Saved". Keep the parameter an
EventCallback: with a plain Action on the same interactive page the
handler ran but the text stayed "Not saved yet", because an EventCallback
re-renders the component that owns the handler and an Action does not.
How it was reproduced
A fresh dotnet new blazor -o FixLab --interactivity Server project on .NET SDK
10.0.401 (ASP.NET Core runtime 10.0.12, no extra packages). A page with no render mode
rendered a Panel with child content and @rendermode="InteractiveServer";
a GET returned 500 with the error above. An Action parameter gave the same
message naming System.Action. An EventCallback parameter gave a
200 and the silent dead button. A string parameter crossed the same boundary without
trouble.
Frequently asked
- Can I pass ChildContent to an InteractiveServer component from a static page?
- No. ChildContent is a RenderFragment, which is a delegate, and delegates cannot be serialized across the render mode boundary. Put the content inside a wrapper component and give the wrapper the render mode, or make the whole page interactive.
- Why does my EventCallback do nothing in an interactive child of a static page?
- EventCallback is a struct, so Blazor does not throw, but the handler cannot travel from the static page into the live circuit. The child receives an empty callback with HasDelegate false and nothing happens. Make the parent interactive so both components share the circuit.
- Which parameters can I pass into an interactive component from a static page?
- Plain data that can be serialized, such as strings and numbers. Delegates, including RenderFragment, Action and Func, cannot cross. If a child needs code from its parent, move the boundary up so both sides are interactive.
More decoded errors in the Fixes category. How the render modes fit together is in Blazor Render Modes: SSR, Server, WebAssembly, and the render mode chooser helps pick one per page.