Have you ever created a Blazor page in a Razor Class Library (RCL) only to get a frustrating 404 error when trying to navigate to it? You're not alone! This is a common issue that catches many developers off guard. Let me walk you through the problem and the solution.
TL;DR
Problem: Blazor pages in Razor Class Libraries return 404 errors even when AdditionalAssemblies
is configured in Routes.razor
.
Solution: Add .AddAdditionalAssemblies()
to your Program.cs
routing configuration:
app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddAdditionalAssemblies(typeof(YourRCL.SomeComponent).Assembly);
You need to configure additional assemblies in both Routes.razor
AND Program.cs
for RCL routing to work properly.
The Problem
I had a Blazor Server app (BlazorApp1
) that referenced a Razor Class Library (RazorClassLibrary1
). In the RCL, I created a simple page:
@page "/page1" <PageTitle>Page 1</PageTitle> <h1>Page 1</h1> <div class="my-component"> This component is defined in the <strong>RazorClassLibrary1</strong> library. </div>
I configured the AdditionalAssemblies
in my Routes.razor
file:
@using System.Reflection <Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(RazorClassLibrary1.Component1).Assembly }"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/> <FocusOnNavigate RouteData="routeData" Selector="h1"/> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="typeof(Layout.MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
Everything looked correct, but navigating to /page1
still returned a 404 error. Frustrating!
The Investigation
To debug this issue, I created a diagnostic page that showed me what routes were being discovered:
@page "/debug" @using System.Reflection @using Microsoft.AspNetCore.Components.Routing <h3>Debug Routes</h3> <h4>Main Assembly Routes:</h4> <ul> @foreach (var route in GetRoutes(typeof(Program).Assembly)) { <li>@route</li> } </ul> <h4>RCL Assembly Routes:</h4> <ul> @foreach (var route in GetRoutes(typeof(RazorClassLibrary1.Component1).Assembly)) { <li>@route</li> } </ul> @code { private List<string> GetRoutes(Assembly assembly) { var routes = new List<string>(); foreach (var type in assembly.GetTypes()) { var routeAttributes = type.GetCustomAttributes<RouteAttribute>(); foreach (var attr in routeAttributes) { routes.Add($"{type.Name}: {attr.Template}"); } } return routes; } }
The debug page showed that the /page1
route was being discovered from the RCL assembly, but the router still wasn't finding it at runtime.
The Solution
The issue was that while the Router
component was configured with AdditionalAssemblies
, the routing system in Program.cs
wasn't aware of the RCL assembly.
The fix is to also register the additional assemblies in Program.cs
:
using BlazorApp1.Components; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapStaticAssets(); app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddAdditionalAssemblies(typeof(RazorClassLibrary1.Component1).Assembly); // 👈 This is the key! app.Run();
Why This Works
The problem occurs because:
- The
Router
component inRoutes.razor
handles client-side routing within the Blazor app - The
MapRazorComponents
inProgram.cs
handles server-side route discovery and registration - Both need to know about the RCL assemblies for the routing to work properly
By adding .AddAdditionalAssemblies()
to the MapRazorComponents
call, we ensure that:
- The server-side routing system discovers all routes from the RCL
- The routes are properly registered with the ASP.NET Core routing system
- The Blazor router can successfully match and render the pages
Complete Working Configuration
Here's the complete working setup:
Program.cs:
app.MapRazorComponents<App>() .AddInteractiveServerRenderMode() .AddAdditionalAssemblies(typeof(RazorClassLibrary1.Component1).Assembly);
Routes.razor:
@using System.Reflection <Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(RazorClassLibrary1.Component1).Assembly }"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/> <FocusOnNavigate RouteData="routeData" Selector="h1"/> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="typeof(Layout.MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
Project Reference in BlazorApp1.csproj:
<ItemGroup> <ProjectReference Include="..\RazorClassLibrary1\RazorClassLibrary1.csproj" /> </ItemGroup>
Key Takeaways
- Both client and server routing need to know about RCL assemblies - Configure both
Routes.razor
andProgram.cs
- The diagnostic page technique is invaluable - Create a debug page to verify route discovery
- This applies to any additional assemblies - Not just RCLs, but any external assemblies containing Blazor components
This solution has saved me hours of debugging, and I hope it helps you too! Have you encountered this issue before? What other Blazor routing gotchas have you discovered?
Found this helpful? Give it a ❤️ and share it with your fellow Blazor developers!
Top comments (0)