Inject content into your Blazor components with typed Render Fragments

Published on

I’ve been doing some more work on the new courseware for https://practicaldotnet.io this week and ran into an interesting use case for Blazor’s Render Fragments.

The application is split up into a couple of different areas.

The course content part of the app (called LearnUI) is entirely separate from the rest of the site, in its own Razor Class Library.

Here’s how the main Blazor app then consumes/displays this LearnUI.

@page "/course/{course}/{*slug}"
<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
</CourseContent>

With this, the main “host” app handles routing, then defers to the CourseContent component (from the Razor Class Library) to actually render something on screen.

As with everything, there are benefits and trade-offs to be considered by having these parts of the UI in separate places but overall I find this logical separation forces me to think a bit more carefully before throwing in markup and logic.

Unlock this course!

But what if we need/want to render content within LearnUI which it doesn’t own?

For example, if someone doesn’t have access to a course, and attempts to view any part of the course, show them an unlock screen, with buttons to go ahead and purchase the course.

That unlock markup and logic doesn’t belong in LearnUI (LearnUI doesn’t have any logic/knowledge of online payments or course prices), so we’d need a way to inject that UI in, and have it shown in place of the course content.

Enter the humble (but extremely useful) RenderFragment

Blazor components can accept one or more RenderFragment parameters, and they mostly do what you would expect; they enable your components to receive a fragment of UI which they can then choose to render.

So, we can make the CourseContent component accept a RenderFragment called LockedUI and render that if the course is locked (the user doesn’t have access).

CourseContent Markup

@if (locked) {
@LockedUI
} else {
@page.Content
}

CourseContent @Code

@code {
private bool locked;
[Parameter]
public RenderFragment LockedUI { get; set; }
...
}

Then, in the consuming page/component, we can define some content to be shown for this LockedUI:

<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<p>Unlock this course now!</p>
</LockedUI>
</CourseContent>

Pass data via the Render Fragment

So far so good, but what if we want to show some more detail about the course that’s locked.

For example, we may want this LockedUI fragment to show the course name, or details about how many lessons are included.

<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<p>Unlock {course name here} now!</p>
</LockedUI>
</CourseContent>

It feels like that information should come from LearnUI as it has the responsibility for retrieving and showing course contents, but how can we tap into that when providing UI for the LockedUI Render Fragment.

The answer is to get CourseContent to expose those details.

We can modify the RenderFragment parameter to be strongly typed, and pass an object “back” when we render it.

@if (locked) {
@LockedUI(course)
}
@code {
CourseDetails course;
[Parameter]
public RenderFragment<CourseDetails> LockedUI { get; set; }
}

In the consuming app, we can access this data via @context.

<CourseContent Course="@Course" Slug="@Slug" OnNodeSelected="@NodeSelected">
<LockedUI>
<h3>Unlock @context.Name</h3>
<p>Includes @context.Curriculum.LessonCount() Lessons</p>
</LockedUI>
</CourseContent>

With that, our consuming app can render the CourseContent component, provide UI to render if the lesson is locked, and easily display any context (data) that the CourseContent component exposes via the LockedUI RenderFragment.

In Summary

You can inject UI into a component using Render Fragments, effectively making a component adaptable, or customisable, in different use cases.

Sometimes you’ll want your UI to reference data from the component which defines the Render Fragment. If so you can make the Render Fragment strongly typed and pass data into it at the point of rendering.

I know you don't have endless hours to learn ASP.NET

Cut through the noise, simplify your web apps, ship your features. One high value email every week.

I respect your email privacy. Unsubscribe with one click.

    Next Up
    1. Using .NET 7's Blazor Custom Elements to render dynamic content
    2. Wait until the last responsible moment to add structure to your Blazor UI
    3. Struggling to get going with a new feature? Start with the HTML