This guide describes how to use content projection to create flexible, reusable components.
Content projection is a pattern in which you insert, or project, the content you want to use inside another component. For example, you could have a Highlight
component that accepts content provided by another component.
The following sections describe common implementations of content projection in Blazor Server.
You can download the example code used in this topic on GitHub.
The most basic form of content projection is single-slot content projection. Single-slot content projection refers to creating a component into which you can project to another component.
To create a component that has a single-slot content projection:
ChildContent
parameter like this.@code { [Parameter] public RenderFragment ChildContent { get; set; } }
The parameter must bepublic
with the typeRenderFragment
and namedChildContent
. This is a fixed syntax and you cannot change any of this.
@ChildContent
where you want the projected content to appear.For example, the following component uses @ChildContent
to highlight a text.
<div style="background-color: green; color: white;"> @ChildContent </div> @code { [Parameter] public RenderFragment ChildContent { get; set; } }
With the @ChildContent
in place, users of this component can now project their own message into the component. For example:
<Highlight> <span>This text comes from parent.</span> <span>Another text comes from parent.</span> </Highlight>
A component can have multiple slots. Each slot can have a RenderFragment
that determines which content goes into that slot. This pattern is referred to as multi-slot content projection. With this pattern, you must specify where you want the projected content to appear.
To create a component that has a multi-slot content projection:
RenderFragment
parameters.@code { [Parameter] public RenderFragment Introduction { get; set; } [Parameter] public RenderFragment Middle { get; set; } [Parameter] public RenderFragment End { get; set; } }
RenderFragment
property where you want the projected content to appear.For example, the following component uses multiple RenderFragment
properties.
<div> @Introduction </div> <h1>Middle</h1> <div> @Middle </div> <h1>End</h1> <div> @End </div> @code { [Parameter] public RenderFragment Introduction { get; set; } [Parameter] public RenderFragment Middle { get; set; } [Parameter] public RenderFragment End { get; set; } }
Users of this component can now project their own content into the component by using render fragment name tag. For example, for the fragment public RenderFragment Introduction
they can use <Introduction>
tag.
<Article> <Introduction> Study Blazor at Blazor School </Introduction> <Middle> Blazor tutorial for beginners, developers, students and for everyone interested. Step by step walk through with sample codes for each step and free downloadable example projects to demonstrate the whole walk through. All example codes are updated to .NET 5. </Middle> <End> There is also an app on Google Play with no ads and free content. You can check it out at <a href="https://play.google.com/store/apps/details?id=com.blazorschool">https://play.google.com/store/apps/details?id=com.blazorschool</a> </End> </Article>
Generics introduces the concept of type parameters to Blazor Server, which makes it possible to design components that defer the specification of one or more types until the component is placed on HTML by the user of component. Let's start with creating a component to display a list of animals and you want each species displayed differently.
Create a new interface IAnimal
, 2 classes Cat
and Dog
in the Data folder.
public interface IAnimal { public string Name { get; set; } public string Breed { get; set; } } public class Dog : IAnimal { public string Name { get; set; } public string Breed { get; set; } public int BondLevel { get; set; } } public class Cat : IAnimal { public string Name { get; set; } public string Breed { get; set; } public int Lives { get; set; } }
Create a new component AnimalList
in the Shared
folder.
<h3>Content projection with type</h3> <div style="color: white"> @foreach (var animal in Animals) { } </div> @code { [Parameter] public List<IAnimal> Animals { get; set; } }
RenderFragment
There are 2 versions of RenderFragment
, the non-generic version RenderFragment
and the generic version RenderFragment<T>
. In order to pass the data to a projecting content, you need to use the RenderFragment<T>
. In this case, those are RenderFragment<Cat>
and RenderFragment<Dog>
.
... @foreach (var animal in Animals) { if (animal is Cat cat) { @CatTemplate(cat); } if (animal is Dog dog) { @DogTemplate(dog); } } @code { ... [Parameter] public RenderFragment<Dog> DogTemplate { get; set; } [Parameter] public RenderFragment<Cat> CatTemplate { get; set; } }
You can also useRenderFragment<T>
forChildContent
<AnimalList Animals="Animals"> </AnimalList> @code { public List<IAnimal> Animals { get; set; } = new() { new Cat() { Name = "Angry", Breed = "Persian", Lives = 4 }, new Dog() { Name = "Doggo", Breed = "Golden Retriever", BondLevel = 2 }, new Cat() { Name = "Hoy", Breed = "Bengal", Lives = 6 }, new Cat() { Name = "Lazy", Breed = "Himalayan", Lives = 7 }, new Dog() { Name = "Woofy", Breed = "Akita", BondLevel = 10 } }; }
Context
to declare a reference to the data in RenderFragment<T>
.<DogTemplate Context="dogCtx"> <div style="background-color: blue"> <div>Cute dog. Its name is @dogCtx.Name type @dogCtx.Breed. Bond level: @dogCtx.BondLevel</div> </div> </DogTemplate>
This image illustrates the data flow for the Context="dogCtx"
.
Do the same thing for CatTemplate
:
<AnimalList Animals="Animals"> <DogTemplate Context="dogCtx"> <div style="background-color: blue"> <div>Cute dog. Its name is @dogCtx.Name type @dogCtx.Breed. Bond level: @dogCtx.BondLevel</div> </div> </DogTemplate> <CatTemplate Context="catCtx"> <div style="background-color: green"> <div>This is a cat. Its name is @catCtx.Name and the breed is @catCtx.Breed. Lives remaining: @catCtx.Lives</div> </div> </CatTemplate> </AnimalList>