Level Up Your Blazor Components with Templates
Table of contents
In my post about creating Blazor components, we went over how to create a simple Button component:
<button @onclick="OnClick" @attributes="Attributes">
@Text
</button>
@code {
[Parameter]
public string Text { get; set; } = "Click me";
[Parameter]
public EventCallback OnClick { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object?>? Attributes { get; set; }
}
What if we want to make this button even more useful? We could write out all of the code to handle an icon, text, or maybe even a whole div of content. There's a better way.
Templated Components
Templated Components are components that take one or more UI templates as parameters to be used in the component. Instead of hard-coding what a consumer can do with your component, we can let them put whatever they want inside with a RenderFragment
. A Render Fragment is a UI template that can consist of HTML, text or more components. When we want to allow child content inside of a component there is a convention we need to follow to make it work. Let's change the Button component above:
<button @onclick="OnClick" @attributes="Attributes">
@ChildContent
</button>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public EventCallback OnClick { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object?>? Attributes { get; set; }
}
You'll notice we changed the string Text
to RenderFragment? ChildContent
. The convention I mentioned is exactly that. You need a Parameter
of type RenderFragment
that is named ChildContent
. If you name the parameter anything else it will not work.
Now that we have made that small change, using it is much like writing HTML and we can put anything we want inside the opening and closing tags:
<Button OnClick="IncrementCount" class="btn btn-primary" title="Button">
<svg height="20" width="20">
<circle cx="10" cy="10" r="5" stroke="black" stroke-width="3" fill="red" />
</svg>
Click Me!
</Button>
Multiple Render Fragments in One Component
There are more complex scenarios where a single ChildContent is not going to be enough for what you are trying to accomplish. You might need multiple Render Fragments to make a robust component.
Fortunately, there is a way to achieve this. Remember when I said the Render Fragment had to be named ChildContent? Well, that is true for that scenario where you just want to be able to use @ChildContent
and have it appear. For components that only have one Render Fragment that works fine.
If you have two or more Render Fragments then it needs to be handled a little differently. You can't give multiple properties the same name. Now you get to name them whatever you want, you just have to add them to your component in a different way. Let's make a Figure component that can do this:
<figure>
@Image
@if (Caption is not null)
{
<figcaption>@Caption</figcaption>
}
</figure>
@code {
[Parameter]
public RenderFragment? Image { get; set; }
[Parameter]
public RenderFragment? Caption { get; set; }
}
So now we have two Render Fragments, one for the image (which could be an <img/>
or <svg>
or anything else) and one for the optional caption. This would also work for something like a table or a form input with a label, anything where you have multiple parts but don't necessarily need multiple components.
Now when we use the Figure component we need to use our Image
and Caption
properties with opening and closing tags inside the Figure
:
<Figure>
<Image>
<img src="http://placekitten.com/300/200" height="200" width="300" />
</Image>
<Caption>
A Cute Kitten
</Caption>
</Figure>
I hope you found this helpful, Happy coding!