You can't use switch expressions in Blazor (sometimes)

6/23/2025
7 minute read

Did you ever try to have a switch expression in Blazor? Yes, and did it go well? Sometimes not. Let's see why!


Update: See the discussion from Ghsot4Man and especially the following ticket: https://github.com/dotnet/razor/issues/7230

The reason is not because Blazor isn't capable of doing so - it is because of that very specific case which starts with a ">". But I am not a big fan of deleting my blog posts, as some parts are true and I wanna live with the mistakes I made 😉


What is Blazor?

If you clicked on this blog post I hope you have a rough understanding what Blazor is. With the question above I meant more: What is Blazor in the eyes of the C# compiler? And the answer is simple: A source generator.

The C# compiler will take your Blazor code (the razor files with all the HTML and your C# logic) and generate a procedural C# code. So let's have a look at this very simple component:

<button @onclick="SayHello">Say Hello</button>
<p>@hello</p>

@code {
    private string hello = string.Empty;

    private void SayHello()
    {
        hello = "Hello, Blazor!";
    }
}

We can have a look into the output by either using Rider or setting the flag in your csproj file:

<PropertyGroup>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>

Now in your obj/Debug/netX.0/generated/Microsoft.CodeAnalysis.Razor.Compiler folder you will find a file with the name of your component - well with the namespace and the name of your component. Let's disect the generated code a bit:

#pragma checksum "/Users/stevengiesel/repos/SwitchBug/Components/Pages/Home.razor" "{8829d00f-11b8-4213-878b-770e8597ac16}" "34d621cb308bef3cc2886146f795062891c1677e803c7368ce32f98de08a9c6d"
// <auto-generated/>
#pragma warning disable 1591
namespace SwitchBug.Components.Pages
{
    #line default
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
#nullable restore
#line (1,2)-(1,23) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using System.Net.Http

#nullable disable
    ;
#nullable restore
#line (2,2)-(2,28) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using System.Net.Http.Json

#nullable disable
    ;
#nullable restore
#line (3,2)-(3,45) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using Microsoft.AspNetCore.Components.Forms

#nullable disable
    ;
#nullable restore
#line (4,2)-(4,47) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using Microsoft.AspNetCore.Components.Routing

#nullable disable
    ;
#nullable restore
#line (5,2)-(5,43) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using Microsoft.AspNetCore.Components.Web

#nullable disable
    ;
#nullable restore
#line (6,2)-(6,61) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using static Microsoft.AspNetCore.Components.Web.RenderMode

#nullable disable
    ;
#nullable restore
#line (7,2)-(7,58) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using Microsoft.AspNetCore.Components.Web.Virtualization

#nullable disable
    ;
#nullable restore
#line (8,2)-(8,27) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using Microsoft.JSInterop

#nullable disable
    ;
#nullable restore
#line (9,2)-(9,17) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using SwitchBug

#nullable disable
    ;
#nullable restore
#line (10,2)-(10,28) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using SwitchBug.Components

#nullable disable
    ;
#nullable restore
#line (11,2)-(11,35) "/Users/stevengiesel/repos/SwitchBug/Components/_Imports.razor"
using SwitchBug.Components.Layout

#nullable disable
    ;
    #line default
    #line hidden
    #nullable restore
    public partial class Home : global::Microsoft.AspNetCore.Components.ComponentBase
    #nullable disable
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
            __builder.OpenElement(0, "button");
            __builder.AddAttribute(1, "onclick", global::Microsoft.AspNetCore.Components.EventCallback.Factory.Create<global::Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, 
#nullable restore
#line (1,19)-(1,27) "/Users/stevengiesel/repos/SwitchBug/Components/Pages/Home.razor"
SayHello

#line default
#line hidden
#nullable disable
            ));
            __builder.AddContent(2, "Say Hello");
            __builder.CloseElement();
            __builder.AddMarkupContent(3, "\n");
            __builder.OpenElement(4, "p");
#nullable restore
#line (2,5)-(2,10) 24 "/Users/stevengiesel/repos/SwitchBug/Components/Pages/Home.razor"
__builder.AddContent(5, hello

#line default
#line hidden
#nullable disable
            );
            __builder.CloseElement();
        }
        #pragma warning restore 1998
#nullable restore
#line (4,8)-(11,1) "/Users/stevengiesel/repos/SwitchBug/Components/Pages/Home.razor"

    private string hello = string.Empty;

    private void SayHello()
    {
        hello = "Hello, Blazor!";
    }

#line default
#line hidden
#nullable disable

    }
}
#pragma warning restore 1591

All of your HTML code is now in the BuildRenderTree method. So the compiler transforms your Blazor code into a procedural C# code.

Switch expressions in C#

Switch expressions were introduced in C# 8.0 and allow you to write more concise and expressive code when dealing with multiple conditions. Here an example:

public string GetGreeting(string name)
{
    return name switch
    {
        "Alice" => "Hello, Alice!",
        "Bob" => "Hello, Bob!",
        _ => "Hello, stranger!"
    };
}

The compiler uses the same technique as with Blazor: It utilizes a source generator to transform the switch expression into a series of if-else statements. The generated code looks like this:

if (!(name == "Alice"))
{
    if (name == "Bob")
    {
        return "Hello, Bob!";
    }
    return "Hello, stranger!";
}
return "Hello, Alice!";

Blazor and switch expressions

Now, let's combine both worlds. What happens if you try to use a switch expression in Blazor? You might expect it to work, but it doesn't. So if you have code like:

<h1>Reading times: @GetReadingTimeRange(10)</h1>

Welcome to your new app.

@code {
    private static string GetReadingTimeRange(int minutes)
    {
        return minutes switch
        {
            < 3 => "< 3",
            < 5 => "3-5",
            < 10 => "5-10",
            < 15 => "10-15",
            < 20 => "15-20",
            _ => "20+"
        };
    }
}

You will get a compilation error like this:

0>Components_Pages_Home_razor.g.cs(142,37): Error CS1525 : Invalid expression term ';'
0>Components_Pages_Home_razor.g.cs(142,37): Error CS1003 : Syntax error, ',' expected
0>Components_Pages_Home_razor.g.cs(143,37): Error CS1003 : Syntax error, '=>' expected
0>Components_Pages_Home_razor.g.cs(143,37): Error CS1525 : Invalid expression term ';'
0>Components_Pages_Home_razor.g.cs(143,37): Error CS1003 : Syntax error, ',' expected
0>Components_Pages_Home_razor.g.cs(144,10): Error CS1002 : ; expected
0>Components_Pages_Home_razor.g.cs(147,2): Error CS1513 : } expected
0>Components_Pages_Home_razor.g.cs(121,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(122,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(123,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(124,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(125,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(126,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(127,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(128,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(129,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(130,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(131,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(132,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(133,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(134,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(135,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(136,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(137,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(138,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(139,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(140,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(141,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(142,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)
0>Components_Pages_Home_razor.g.cs(143,13): Error CS0246 : The type or namespace name '__builder' could not be found (are you missing a using directive or an assembly reference?)

Soooooo a lot of errors. The problem with the current version (net10.0) is that a source code generator can not see the output of another source code generator. So that leaves you with a problem: Even if the switch expression is transformed into a series of if-else statements, the Blazor source generator does not see that output and see's the "invalid" switch expression that the underlying C# compiler doesn't understand.

Workaround

Well, either you use the old if-else statements or you can use switch statements.

An error has occurred. This application may no longer respond until reloaded. Reload x