Dynamically-rendered ASP.NET Core Razor components
Note
This isn't the latest version of this article. To switch to the latest, use the ASP.NET Core version selector at the top of the table of contents.
If the selector isn't visible in a narrow browser window, widen the window or select the vertical ellipsis (⋮) > Table of contents.
By Dave Brock
Use the built-in DynamicComponent component to render components by type.
A DynamicComponent is useful for rendering components without iterating through possible types or using conditional logic. For example, DynamicComponent can render a component based on a user selection from a dropdown list.
In the following example:
componentType
specifies the type.parameters
specifies component parameters to pass to thecomponentType
component.
<DynamicComponent Type="@componentType" Parameters="@parameters" />
@code {
private Type componentType = ...;
private IDictionary<string, object> parameters = ...;
}
For more information on passing parameter values, see the Pass parameters section later in this article.
Use the Instance property to access the dynamically-created component instance:
<DynamicComponent Type="@typeof({COMPONENT})" @ref="dc" />
<button @onclick="Refresh">Refresh</button>
@code {
private DynamicComponent? dc;
private Task Refresh()
{
return (dc?.Instance as IRefreshable)?.Refresh();
}
}
In the preceding example:
- The
{COMPONENT}
placeholder is the dynamically-created component type. IRefreshable
is an example interface provided by the developer for the dynamic component instance.
Example
In the following example, a Razor component renders a component based on the user's selection from a dropdown list of four possible values.
User spaceflight carrier selection | Shared Razor component to render |
---|---|
Rocket Lab® | Shared/RocketLab.razor |
SpaceX® | Shared/SpaceX.razor |
ULA® | Shared/UnitedLaunchAlliance.razor |
Virgin Galactic® | Shared/VirginGalactic.razor |
Shared/RocketLab.razor
:
<h2>Rocket Lab®</h2>
<p>
Rocket Lab is a registered trademark of
<a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>
Shared/SpaceX.razor
:
<h2>SpaceX®</h2>
<p>
SpaceX is a registered trademark of
<a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>
Shared/UnitedLaunchAlliance.razor
:
<h2>United Launch Alliance®</h2>
<p>
United Launch Alliance and ULA are registered trademarks of
<a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>
Shared/VirginGalactic.razor
:
<h2>Virgin Galactic®</h2>
<p>
Virgin Galactic is a registered trademark of
<a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>
Pages/DynamicComponentExample1.razor
:
@page "/dynamiccomponent-example-1"
<h1><code>DynamicComponent</code> Component Example 1</h1>
<p>
<label>
Select your transport:
<select @onchange="OnDropdownChange">
<option value="">Select a value</option>
<option value="@nameof(RocketLab)">Rocket Lab</option>
<option value="@nameof(SpaceX)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance)">ULA</option>
<option value="@nameof(VirginGalactic)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType" />
</div>
}
@code {
private Type? selectedType;
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
Shared/RocketLab.razor
:
<h2>Rocket Lab®</h2>
<p>
Rocket Lab is a registered trademark of
<a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>
Shared/SpaceX.razor
:
<h2>SpaceX®</h2>
<p>
SpaceX is a registered trademark of
<a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>
Shared/UnitedLaunchAlliance.razor
:
<h2>United Launch Alliance®</h2>
<p>
United Launch Alliance and ULA are registered trademarks of
<a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>
Shared/VirginGalactic.razor
:
<h2>Virgin Galactic®</h2>
<p>
Virgin Galactic is a registered trademark of
<a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>
Pages/DynamicComponentExample1.razor
:
@page "/dynamiccomponent-example-1"
<h1><code>DynamicComponent</code> Component Example 1</h1>
<p>
<label>
Select your transport:
<select @onchange="OnDropdownChange">
<option value="">Select a value</option>
<option value="@nameof(RocketLab)">Rocket Lab</option>
<option value="@nameof(SpaceX)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance)">ULA</option>
<option value="@nameof(VirginGalactic)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType" />
</div>
}
@code {
private Type? selectedType;
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
In the preceding example:
- Component names are used as the option values using the
nameof
operator, which returns component names as constant strings. - The namespace of the app is
BlazorSample
. Change the namespace to match your app's namespace.
Pass parameters
If dynamically-rendered components have component parameters, pass them into the DynamicComponent as an IDictionary<string, object>
. The string
is the name of the parameter, and the object
is the parameter's value.
The following example configures a component metadata object (ComponentMetadata
) to supply parameter values to dynamically-rendered components based on the type name. The example is just one of several approaches that you can adopt. Parameter data can also be provided from a web API, a database, or a method. The only requirement is that the approach returns an IDictionary<string, object>
.
ComponentMetadata.cs
:
public class ComponentMetadata
{
public string? Name { get; set; }
public Dictionary<string, object> Parameters { get; set; } =
new Dictionary<string, object>();
}
The following RocketLabWithWindowSeat
component (Shared/RocketLabWithWindowSeat.razor
) has been updated from the preceding example to include a component parameter named WindowSeat
to specify if the passenger prefers a window seat on their flight:
Shared/RocketLabWithWindowSeat.razor
:
<h2>Rocket Lab®</h2>
<p>
User selected a window seat: @WindowSeat
</p>
<p>
Rocket Lab is a trademark of
<a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>
@code {
[Parameter]
public bool WindowSeat { get; set; }
}
<h2>Rocket Lab®</h2>
<p>
User selected a window seat: @WindowSeat
</p>
<p>
Rocket Lab is a trademark of
<a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>
@code {
[Parameter]
public bool WindowSeat { get; set; }
}
In the following example:
- Only the
RocketLabWithWindowSeat
component's parameter for a window seat (WindowSeat
) receives the value of theWindow Seat
checkbox. - The namespace of the app is
BlazorSample
. Change the namespace to match your app's namespace. - The dynamically-rendered components are shared components in the app's
Shared
folder:- Shown in this article section:
RocketLabWithWindowSeat
(Shared/RocketLabWithWindowSeat.razor
) - Components shown in the Example section earlier in this article:
SpaceX
(Shared/SpaceX.razor
)UnitedLaunchAlliance
(Shared/UnitedLaunchAlliance.razor
)VirginGalactic
(Shared/VirginGalactic.razor
)
- Shown in this article section:
Pages/DynamicComponentExample2.razor
:
@page "/dynamiccomponent-example-2"
<h1><code>DynamicComponent</code> Component Example 2</h1>
<p>
<label>
<input type="checkbox" @bind="WindowSeat" />
Window Seat (Rocket Lab only)
</label>
</p>
<p>
<label>
Select your transport:
<select @onchange="OnDropdownChange">
<option value="">Select a value</option>
@foreach (var c in components)
{
<option value="@c.Key">@c.Value.Name</option>
}
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@components[selectedType.Name].Parameters" />
</div>
}
@code {
private Dictionary<string, ComponentMetadata> components =
new()
{
{
"RocketLabWithWindowSeat",
new ComponentMetadata
{
Name = "Rocket Lab with Window Seat",
Parameters = new() { { "WindowSeat", false } }
}
},
{
"VirginGalactic",
new ComponentMetadata { Name = "Virgin Galactic" }
},
{
"UnitedLaunchAlliance",
new ComponentMetadata { Name = "ULA" }
},
{
"SpaceX",
new ComponentMetadata { Name = "SpaceX" }
}
};
private Type? selectedType;
private bool windowSeat;
private bool WindowSeat
{
get { return windowSeat; }
set
{
windowSeat = value;
components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] =
windowSeat;
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
@page "/dynamiccomponent-example-2"
<h1><code>DynamicComponent</code> Component Example 2</h1>
<p>
<label>
<input type="checkbox" @bind="WindowSeat" />
Window Seat (Rocket Lab only)
</label>
</p>
<p>
<label>
Select your transport:
<select @onchange="OnDropdownChange">
<option value="">Select a value</option>
@foreach (var c in components)
{
<option value="@c.Key">@c.Value.Name</option>
}
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@components[selectedType.Name].Parameters" />
</div>
}
@code {
private Dictionary<string, ComponentMetadata> components =
new()
{
{
"RocketLabWithWindowSeat",
new ComponentMetadata
{
Name = "Rocket Lab with Window Seat",
Parameters = new() { { "WindowSeat", false } }
}
},
{
"VirginGalactic",
new ComponentMetadata { Name = "Virgin Galactic" }
},
{
"UnitedLaunchAlliance",
new ComponentMetadata { Name = "ULA" }
},
{
"SpaceX",
new ComponentMetadata { Name = "SpaceX" }
}
};
private Type? selectedType;
private bool windowSeat;
private bool WindowSeat
{
get { return windowSeat; }
set
{
windowSeat = value;
components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] =
windowSeat;
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
Event callbacks (EventCallback
)
Event callbacks (EventCallback) can be passed to a DynamicComponent in its parameter dictionary.
ComponentMetadata.cs
:
public class ComponentMetadata
{
public string? Name { get; set; }
public Dictionary<string, object> Parameters { get; set; } =
new Dictionary<string, object>();
}
Implement an event callback parameter (EventCallback) within each dynamically-rendered component.
Shared/RocketLab2.razor
:
<h2>Rocket Lab®</h2>
<p>
Rocket Lab is a registered trademark of
<a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
@code {
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
Shared/SpaceX2.razor
:
<h2>SpaceX®</h2>
<p>
SpaceX is a registered trademark of
<a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
@code {
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
Shared/UnitedLaunchAlliance2.razor
:
<h2>United Launch Alliance®</h2>
<p>
United Launch Alliance and ULA are registered trademarks of
<a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
@code {
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
Shared/VirginGalactic2.razor
:
<h2>Virgin Galactic®</h2>
<p>
Virgin Galactic is a registered trademark of
<a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>
<button @onclick="OnClickCallback">
Trigger a Parent component method
</button>
@code {
[Parameter]
public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
In the following parent component example, the ShowDTMessage
method assigns a string with the current time to message
, and the value of message
is rendered.
The parent component passes the callback method, ShowDTMessage
in the parameter dictionary:
- The
string
key is the callback method's name,OnClickCallback
. - The
object
value is created by EventCallbackFactory.Create for the parent callback method,ShowDTMessage
. Note that thethis
keyword isn't supported in C# fields, so a C# property is used for the parameter dictionary.
For the following component, change the namespace name of BlazorSample
in the OnDropdownChange
method to match your app's namespace.
Pages/DynamicComponentExample3.razor
:
@page "/dynamiccomponent-example-3"
<h1><code>DynamicComponent</code> Component Example 3</h1>
<p>
<label>
Select your transport:
<select @onchange="OnDropdownChange">
<option value="">Select a value</option>
<option value="@nameof(RocketLab2)">Rocket Lab</option>
<option value="@nameof(SpaceX2)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance2)">ULA</option>
<option value="@nameof(VirginGalactic2)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@Components[selectedType.Name].Parameters" />
</div>
}
<p>
@message
</p>
@code {
private Type? selectedType;
private string? message;
private Dictionary<string, ComponentMetadata> Components
{
get
{
return new Dictionary<string, ComponentMetadata>()
{
{
"RocketLab2",
new ComponentMetadata
{
Name = "Rocket Lab",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"VirginGalactic2",
new ComponentMetadata
{
Name = "Virgin Galactic",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"UnitedLaunchAlliance2",
new ComponentMetadata
{
Name = "ULA",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"SpaceX2",
new ComponentMetadata
{
Name = "SpaceX",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
}
};
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
private void ShowDTMessage(MouseEventArgs e) =>
message = $"The current DT is: {DateTime.Now}.";
}
Avoid catch-all parameters
Avoid the use of catch-all parameters. If catch-all parameters are used, every explicit parameter on DynamicComponent effectively is a reserved word that you can't pass to a dynamic child. Any new parameters passed to DynamicComponent are a breaking change, as they start shadowing child component parameters that happen to have the same name. It's unlikely that the caller always knows a fixed set of parameter names to pass to all possible dynamic children.
Trademarks
Rocket Lab is a registered trademark of Rocket Lab USA Inc. SpaceX is a registered trademark of Space Exploration Technologies Corp. United Launch Alliance and ULA are registered trademarks of United Launch Alliance, LLC. Virgin Galactic is a registered trademark of Galactic Enterprises, LLC.
Additional resources
Feedback
Submit and view feedback for