We will learn what templated components are and how they help us implement more reusable Blazor components.
In this article, we learn what templated components are in Blazor web development. We also implement a Dialog component that can be templated and used throughout a Blazor web application.
You can access the code used in this example on GitHub.
Templated Components allow us to provide an HTML snipped from one Blazor component to another component. This concept is widely used in component-oriented user interface libraries.
It allows us to build higher-order components, where we have a common task that we want to solve in a component but allows the consuming component to customize its appearance.
A component can have one or more templated parameters. We use the RenderFragement
type to define and use them.
Let’s start with a practical example illustrating how to implement a templated component. In this article, we build a Dialog
component.
The Dialog
component defines a header, a body and a buttons section. In a real application, a dialog component is a perfect example where you want a consistent look and feel throughout the application but allow custom content for each use case.
We create a Dialog.razor
file and add the following template code:
<h2>@Title</h2>
<div>@Body</div>
<div style="display: flex; margin-top: 10px;">
@if (Buttons != null)
{
foreach (var button in Buttons)
{
<button style="margin-right: 10px;"
onclick="@button.Action">
@button.Text
</button>
}
}
</div>
First, we use an HTML header element to display the title of the dialog and a div
element to render the body of the dialog. We will explore the differences between the two below, where we go through the code section of the Dialog
component.
The last section of the template is more complex. We use a div
element and use some C# code to check the Button
property for null. Next, we use a foreach
statement to loop through the Buttons
property and render a button
element for each item in the Buttons
list.
Now, let’s implement the Dialog
component’s code section.
@code {
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? Body { get; set; }
}
First, we have two properties marked with the Parameter
attribute. It tells Blazor to accept a value when using the component. You can learn more about parameter usage in the Creating a Blazor Component article.
The difference between the two parameters is that we use the string?
type for the Title
property and the RenderFragement
type for the Body
property.
The RenderFragment
type is what allows us to define a property that contains a part of the component template. We will see how to pass that value when using the Dialog
component shortly.
As you can see, using the values for both properties within the component’s template is simple. We use the @
symbol to reference the properties. There is no difference in usage between a string
-typed and a RenderFragement
-typed component parameter property.
The only thing missing in the code section that we use in the template is the Buttons
property. Let’s add the missing code to the code section.
@code {
public record DialogButton(string Text, Action Action);
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? Body { get; set; }
[Parameter]
public IEnumerable<DialogButton>? Buttons { get; set; }
}
We use the record
keyword to create a DialogButton
type. It contains two properties—a Text
property of type string
, and an Action
property of type Action
.
We also define the missing Buttons
property of type IEnumerable<DialogButton>
and add the Parameter
attribute.
We’ll see how we can provide custom buttons using the DialogButton
type next.
Now that we have a Dialog
component, we want to implement a first use case.
Look at the following code implemented on the Index
component of the project. You can use the Dialog
component throughout the entire project.
@page "/"
<PageTitle>Basic Dialog</PageTitle>
<Dialog Title="Are you sure?" Buttons="@Buttons">
<Body>Do you really want to delete this user?</Body>
</Dialog>
First of all, use the @page
directive to define this Blazor component as a page component. The "/"
defines that this component should be loaded when the user navigates to the index route of the web application.
Next, we use the built-in PageTitle
component to provide custom text to the browser tab.
Now, finally, to the usage of the Dialog
.
We reference the Dialog
component and provide values for the Title
and the Buttons
properties. So far, so good.
Now, to the interesting part of how to provide a template to the Body
property of the Dialog
component. As you can see, we use a new tag Body
and enclose the template we want to provide to the templated Dialog
component.
In this case, we only provide text. However, we will implement a more advanced example later. Let’s first learn how it works before we get crazy.
Let’s implement the component’s code section next.
@code {
public IEnumerable<DialogButton> Buttons { get; set; } = new List<DialogButton>{
new DialogButton("Delete", () =>
{
Debug.WriteLine("Delete");
}),
new DialogButton("Cancel", () =>
{
Debug.WriteLine("Cancel");
})
};
}
We define a Buttons
property of type IEnumerable<DialogButton>
and create and add two elements to the list. We define a Delete and a Cancel button. We also provide a simple log statement as the action to each button.
To make this code compile, we need to add two additional using
statements at the top of the component:
@using static BlazorTemplatedComponents.Shared.Dialog;
@using System.Diagnostics;
The first using
statement imports the namespace containing the DialogButton
type. The second using statement contains the Debug.WriteLine
method. It allows us to write to the Output tab in Visual Studio or to any other log sink when using another IDE.
Now, let’s start the application, and we can see the values provided in the Index
component applied to the Dialog
component on the screen.
But what if we want to provide a more complex template to the templated Dialog
component than simple text?
Let’s look at the following more advanced usage of the Dialog
component.
<Dialog Title="Do you want to move this team?" Buttons="@Buttons">
<Body>
<h4>Team members</h4>
<table>
<thead>
<tr>
<th width="200">Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
@foreach (var member in Members)
{
<tr>
<td>@member.Name</td>
<td>@member.Age</td>
</tr>
}
</tbody>
</table>
</Body>
</Dialog>
This time, we want to ask the user whether they want to move an existing team to another organization.
To make it obvious how many and which team members get moved, we show them in a table provided as part of the Dialog
component’s templated Body
property.
We can use all the Blazor and C# code we are used to when working with non-templated components.
Let’s look at the component’s code section:
@code {
public record TeamMember(string Name, int Age);
public IEnumerable<TeamMember> Members { get; set; } = new List<TeamMember>{
new TeamMember("Peter", 37),
new TeamMember("Jessica", 32)
};
public IEnumerable<DialogButton> Buttons { get; set; } = new List<DialogButton>{
new DialogButton("OK", () =>
{
Debug.WriteLine("OK");
}),
new DialogButton("Cancel", () =>
{
Debug.WriteLine("Cancel");
})
};
}
Again, we provide two buttons using the DialogButton
type to the Button
property of the Dialog
component. However, the content of the Body
element is widely different.
We define a Members
property in the component code. In a real application, we might have loaded that information from a backend service.
Next, we reference the Members
property in the template code and iterate through the members list to render a table row for each team member.
Again, we start the application to see the rendered Dialog on the screen.
This time, we see the more advanced Body
section of the Dialog
component rendering an HTML table
with a row per team member.
Templated components are a great tool to implement reusable and higher-order components when building Blazor web applications.
We can have one or multiple parameters of type RenderFragment
per component and, therefore, provide one or more templates to Blazor components.
Templated component helps us provide a consistent look and feel and a consistent user experience throughout the entire web application, while still allowing customization where needed.
You can access the code used in this example on GitHub.
If you want to learn more about Blazor development, you can watch my free Blazor Crash Course on YouTube. And stay tuned to the Telerik blog for more Blazor Basics.
Claudio Bernasconi is a passionate software engineer and content creator writing articles and running a .NET developer YouTube channel. He has more than 10 years of experience as a .NET developer and loves sharing his knowledge about Blazor and other .NET topics with the community.