Content projection

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.

  • Single-slot content projection: A component which only has one slot of content to project.
  • Multiple-slot content projection: A component which has multiple slots of content to project.
  • Content projection with generic type: Make the projected content more dynamic by having a generic type.
You can download the example code used in this topic on GitHub.

Single-slot content projection

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:

  1. Create a component.
  2. Declare a ChildContent parameter like this.
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}
The parameter must be public with the type RenderFragment and named ChildContent. This is a fixed syntax and you cannot change any of this.
  1. In the template of your component, add @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>

Multi-slot content projection

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:

  1. Create a component.
  2. Declare RenderFragment parameters.
@code {
    [Parameter]
    public RenderFragment Introduction { get; set; }
    [Parameter]
    public RenderFragment Middle { get; set; }
    [Parameter]
    public RenderFragment End { get; set; }
}
  1. In the template of your component, add 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>

Content projection with generic type

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.

Creating models

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; }
}

Creating a content projection component

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; }
}

Declaring and passing data to a 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 use RenderFragment<T> for ChildContent

Using generic content projection component

  1. Initilize and pass the data to the component.
<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
        }
    };
}
  1. Use 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>
BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2025 Blazor School
An unhandled error has occurred. Reload 🗙