Dynamically-rendered ASP.NET Core Razor components
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