Is it possible to render components "dynamically" using Blazor?

November 11, 2020 · 10 minute read · Tags: blazor

Nothing twists a developer’s brain more than things being ‘dynamic’ (except perhaps, recursion).

It’s all very nice and predictable when you can look at the code and see exactly which components are being rendered on any given page.

But when you start building your web applications using Blazor you might get to a point where you decide you need to render your components ‘dynamically’.

This usually means you need the application to render different things based on data (in other words the decision about which component(s) to render occurs at run-time and not compile-time).

Say for example you want to build a customisable dashboard where your users can pick and choose the widgets they want displayed.

See Dynamic Components In Action

How do you actually use dynamic components in a real app?

Pop your email in the box below and I'll ping over the link for the subscribers only vault, which includes step-by-step tutorials, videos and source code (including a fully working version of a dynamic dashboard using .NET 6's DynamicComponent)

I respect your email privacy. Unsubscribe with one click.

    It turns out there are a few ways to do this in Blazor, and one much easier way if you’re using .NET 6.

    First, here are some ways to solve this problem using .NET 5 or earlier:

    @foreach loop and switch statement

    One option is to create a List of your components, loop over them and use a switch statement to render the relevant component…

    @page "/SimpleDashboard"
    
    @foreach (var widget in _components)
    {
        switch (widget)
        {
            case WidgetA:
                <WidgetA/>
                break;
            case WidgetB:
                <WidgetB/>
                break;
        }
    }
    
    @code {
        readonly List<IComponent> _components = new List<IComponent>
        {
            new WidgetA(),
            new WidgetB()
        };
    }
    

    Here we’ve a List which can contain any object which implements the IComponent interface.

    The switch takes care of figuring out what to render by essentially checking the type of each item in the list.

    With this approach you can create any component you like, add a case to the switch statement and then if an instance of that component appears in the list the relevant arm of the switch statement will take care of rendering the component.

    This works but does carry a few downsides.

    Firstly it feels a bit redundant to create new instances of the components to add them to the _components list. In reality we never use these instances other than to evaluate their type as part of the switch statement.

    The other real downside is the switch statement itself will grow as you add new components. Plus you will need to come back and deploy new code to handle these new component types, so you could end up with a situation where the data refers to a widget you haven’t handled yet.

    Saying that, this is conceptually the simplest way to achieve this kind of pseudo-polymorphism, and unless you’re coming up with new component types on a regular basis the cost of spending a few moments to wire up a new branch in the switch statement is probably manageable.

    RenderFragments declared in code

    Another option is to declare RenderFragments rather than separate components.

    @page "/LessSimpleDashboard"
    
    @foreach (var widget in _components)
    {
        @widget
    }
    
    @code {
        readonly List<RenderFragment> _components = new List<RenderFragment>
        {
            RenderWidgetA,
            RenderWidgetB
        };
    
        static readonly RenderFragment RenderWidgetA = __builder =>
        {
            <h3>Widget A</h3>
            <p>This is a widget</p>
        };
    
        static readonly RenderFragment RenderWidgetB = __builder =>
        {
            <h3>Widget B</h3>
            <p>This is another widget</p>
        };
    }
    

    With this approach we can declare anything we like in each render fragment and add as many of them as we like to the list.

    Then we foreach over each RenderFragment in the list and render each one!

    This gets rid of the switch statement but still provides plenty of flexibility in terms of what you render and how.

    The obvious downside is that you end up with all your UI code lumped together in one place and lose the benefits that come from separating your UI into separate components. Generally speaking separate components will be easier to maintain and reason about than a bunch of RenderFragment declarations.

    It’s worth noting there are some performance considerations when it comes to creating components vs RenderFragments. If you want to find out more about those check out this handy list of best practices for Blazor WASM from the official docs.

    Build your own Render Tree

    Finally we have an option which comes with a big caveat.

    This approach uses fairly low-level APIs which you wouldn’t typically make use of when building apps using Blazor and .NET 6 ships with a new DynamicComponent which essentially renders this approach redundant.

    Saying that, this option is technically possible right now and shines a light on how Blazor renders components which is useful to know anyway!

    When you build your Blazor app you generally declare components using the Razor templating language.

    But under the hood there are a few more steps before your UI is rendered in the browser.

    Updating the DOM is an expensive operation for the browser; CPU intensive and slow (in relative terms).

    Blazor takes a similar approach to other frameworks like React whereby it keeps an in-memory representation of the DOM elements. This is represented as a tree of HTML elements.

    You can see the initial render tree if you head to the obj folder after building a Blazor project.

    In obj/Debug/net5.0/Razor (if you’re using .NET 5) you’ll find folders representing your application.

    Here’s part of the standard Counter example as it appears in obj/Debug/net5.0/Razor/Pages/Counter.razor.g.cs.

     protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
            {
                __builder.AddMarkupContent(0, "<h1>Counter</h1>\r\n\r\n");
                __builder.OpenElement(1, "p");
                __builder.AddContent(2, "Current count: ");
    			__builder.AddContent(3, currentCount);
    			__builder.CloseElement();
    			
    			// other code omitted
    		}
    

    So you can see that although we generally write our components using Razor, Blazor is ultimately going to express that Razor code as a RenderTreeBuilder.

    As your users interact with your UI new render trees will be generated in memory.

    There are a couple of benefits to this virtual DOM.

    Firstly it can be updated many times (when executing logic, as various components are updated based on new parameters etc.) without performing expensive browser DOM updates until everything’s figured out. This means you get one DOM update at the end, rather than lots of DOM updates as a component (or tree of components) renders.

    Secondly Blazor can perform incremental rendering. This is where it compares the two Render Trees (the old one and the new one), figures out what’s changed then makes the smallest number of changes possible to update the DOM to match the new tree.

    Typically you wouldn’t write RenderTree logic yourself, but it can be handy when declaring ‘dynamic’ components.

    Here’s an example:

    @page "/dashboard"
    
    @foreach (var widget in _widgets)
    {
        <h1>@widget.Key</h1>
        @renderWidget(widget.Value)
    }
    
    @code {
    
        readonly Dictionary<string, Type> _widgets = new Dictionary<string, Type>
        {
            ["Counter"] = typeof(Counter),
            ["Weather"] = typeof(FetchData)
        };
    
        private RenderFragment renderWidget(Type t) => builder =>
        {
            builder.OpenComponent(0, t);
            builder.CloseComponent();
        };
    
    }
    

    Here we’ve declared a dictionary which has a string key (essentially the name or label for the widget) and a Type value.

    With this we can add as many ‘widgets’ as we like.

    In this case I’ve stuck to the default Counter and FetchData components. renderWidget returns a delegate which accepts a RenderTreeBuilder parameter.

    From here you can build your own tree however you see fit!

    In this example we simply open the component (using the Type passed in from the dictionary) then close it. This is a very procedural way of writing your components but in this case it means we can throw any component (of any Type) into the dictionary and it will be rendered accordingly.

    The obvious caveat is this doesn’t currently set any parameters for the component.

    You can set parameters easily enough:

    private RenderFragment renderWidget(Type t) => builder =>
    {
    	builder.OpenComponent(0, t);
    	builder.AddAttribute(1, "some-parameter", "a value");
    	builder.CloseComponent();
    };
    

    The trick would be knowing what parameters are available for the various components you wish to render.

    This is something that the proposed DynamicComponent would handle by accepting a dictionary of parameters.

    A Simpler Option (.NET 6)

    .NET 6 introduces a much simpler way to tackle this problem with the brand new DynamicComponent.

    With this you can render a component using its type. So for example if you store a list of types:

    @code {
        readonly List<Type> _components = new List<Type>
        {
            typeof(WidgetA),
            typeof(WidgetB)
        };
    }
    

    You can then render a component for each type using DynamicComponent:

    @foreach(var component in _components){
        <DynamicComponent Type="@component"/>
    }
    

    With this it’s now much easier to render components from data, dynamically.

    But what if you wanted to pass some extra parameters to the component? Turns out you can do that too, using DynamicComponent’s Parameters parameter.

    @foreach(var component in _components){
        <DynamicComponent Type="@component" Parameters="..."/>
    }
    

    This Parameters parameter (that’s a lot of parameters!) expects a dictionary where the key is a string and the value an object, like this:

    private Dictionary<string, object> exampleParameters 
        = new Dictionary<string, object>
        {
            ["Title"] = "A fancy component"
        };
    

    Were you to assign this to the dynamic component’s Parameters parameter, and assuming a Title parameter exists on the component you’re attempting to render, it would be populated with the value from the dictionary (‘A fancy Component’).

    See Dynamic Components In Action

    How do you actually use dynamic components in a real app?

    Pop your email in the box below and I'll ping over the link for the subscribers only vault, which includes step-by-step tutorials, videos and source code (including a fully working version of a dynamic dashboard using .NET 6's DynamicComponent)

    I respect your email privacy. Unsubscribe with one click.

      In Summary

      There are a few different ways to render components when you don’t know what you need to render at compile time.

      Again it’s worth reiterating you can often take a simpler approach with Blazor.

      A simple foreach and static component declaration will often do the job, but occasionally you’ll run into more complex requirements.

      The new DynamicComponent in .NET 6 is by far the simplest approach, but whatever you end up adopting it pays to keep it simple, and limit how much ‘dynanism’ you incorporate into your code.

      Just because you can doesn’t mean you should, especially when it comes to building your own render tree.

      That said, it’s good to know what options exists if you find yourself going down this path!

      References

      For more on the Virtual and Incremental DOMs check out this handy explanation from Blazor University.

      ASP.NET Core Blazor WebAssembly performance best practices outlines a few tips for optimising your Blazor WASM applications for performance and also covers declaring RenderFragments in @code (rather than separate components).

      Next up

      How to upload a file with Blazor SSR in .NET 8?
      How to handle file uploads without using an interactive render mode?
      3 simple design tips to improve your Web UI
      Spruce up your features
      The quickest way to integrate PayPal checkout with Blazor SSR in .NET 8
      JavaScript Interop works differently with Blazor Server-side rendering