blazor-renderfragments-childcontent

RenderFragment in Blazor: Passing Content to a Component

  • 4 min

A RenderFragment is a delegate that represents a piece of UI that Blazor can render.

In the previous article, we passed data from parent to child. If a Ventana.razor component received its content as a string, it could only display text or would have to interpret HTML, with the security and maintenance issues that implies.

With RenderFragment, we can pass HTML, components, and Razor expressions without converting them into a string. The concept is similar to slots in other frameworks.

The Standard: ChildContent

Blazor follows a convention: if you define a parameter of type RenderFragment and name it exactly ChildContent, it captures everything you place between the component’s opening and closing tags.

Let’s create a simple container component: AlertBox.razor.

Component Definition (AlertBox.razor)

<div class="alert alert-info">
    <h4 class="alert-heading">Attention!</h4>

    <div class="alert-body">
        @ChildContent
    </div>
</div>

@code {
    // The name MUST be EXACTLY ChildContent for the implicit behavior to work
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
Copied!

Usage from the Parent

Now, when using it, we don’t need to assign the ChildContent="..." attribute. We simply write HTML inside.

<AlertBox>
    <p>This is a paragraph <strong>with bold text</strong> passed as a parameter.</p>
    <button class="btn btn-sm btn-danger">Even functional buttons</button>
</AlertBox>
Copied!

The AlertBox component defines the container (border, color, and title), but delegates the inner content to the parent component.

Multiple Named Fragments

What if a single slot isn’t enough? Imagine a Card component that needs a slot for the Header, another for the Body, and another for the Footer.

We can define as many RenderFragment parameters as we want. The only difference is that, when using them, we must specify which slot each piece of content goes into.

The Component (MyCard.razor):

<div class="card">
    <div class="card-header">
        @* If the user doesn't pass a Header, we can show a default or nothing *@
        @if (Header != null)
        {
            @Header
        }
        else
        {
            <span>Default Title</span>
        }
    </div>

    <div class="card-body">
        @Body
    </div>

    <div class="card-footer text-muted">
        @Footer
    </div>
</div>

@code {
    [Parameter] public RenderFragment? Header { get; set; }
    [Parameter, EditorRequired] public RenderFragment Body { get; set; } = default!;
    [Parameter] public RenderFragment? Footer { get; set; }
}
Copied!

The Usage:

Now we use nested XML tags that match the names of our parameters.

<MyCard>
    <Header>
        <h3>User: Luis</h3>
    </Header>

    <Body>
        <p>Here goes the main content of the card.</p>
        <img src="avatar.png" alt="Avatar of Luis" />
    </Body>

    <Footer>
        <button>View Profile</button>
    </Footer>
</MyCard>
Copied!

Notice that we no longer use the implicit ChildContent property, but explicit tags like <Header>, <Body>, etc. This greatly improves code readability.

Components with Generic Templates

So far, the parent defined the HTML and the child decided where to render it. Generic components extend this idea to work with data of any type.

There is also a more complex scenario: generic list or table components.

Consider a Listado<T> component.

  • The component knows how to iterate the list and lay out the grid.
  • But the component doesn’t know which properties of T to display (because T could be a Product, a User, or a Car).

For this, we use RenderFragment<T>. This allows the child to pass data back to the parent so the parent can decide how to render it.

The Component (GenericList.razor):

@typeparam TItem

<div class="fancy-list">
    @foreach (var item in Items)
    {
        <div class="item-row">
            @ItemTemplate(item)
        </div>
    }
</div>

@code {
    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; } = [];

    // This fragment accepts a parameter of type TItem
    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; } = default!;
}
Copied!

The Usage:

When using the component, we define the template and receive the object through the context variable (by default context).

<h3>Product List</h3>

<GenericList Items="@myProducts">
    <ItemTemplate>
        <strong>@context.Name</strong> - <span>@context.Price €</span>
    </ItemTemplate>
</GenericList>

<h3>User List</h3>

<GenericList Items="@myUsers">
        <ItemTemplate Context="user">
          <div class="user-badge">
            <img src="@user.Avatar" alt="Avatar of @user.Alias" /> @user.Alias
        </div>
    </ItemTemplate>
</GenericList>
Copied!

This technique is the foundation of complex components like DataGrids, Virtualizers, or any advanced UI control you see in third-party libraries.