Telerik blogs

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.

Introduction

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.

Implementing a Templated Component

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.

Using a Templated Component

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.

A Blazor application with a templated dialog component. It contains a title, a question and two buttons: Delete, and Cancel.

A More Advanced Scenario for Template Components

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.

A Blazor application with a templated dialog component. It contains a title, an advanced Body section containing a table with all team members, and two buttons: OK, and Cancel.

This time, we see the more advanced Body section of the Dialog component rendering an HTML table with a row per team member.

Conclusion

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.


About the Author

Claudio Bernasconi

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.

Related Posts

Comments

Comments are disabled in preview mode.