Programming

Integrate RCL Pages in Blazor Server with Dynamic Loading

Fix Blazor Server routing for Razor Class Library pages like /dashboard using dynamic assembly loading. Add assemblies to Router AdditionalAssemblies and MapRazorComponents.AddAdditionalAssemblies for full route discovery in modular .NET 8+ apps.

5 answers 1 view

How to integrate Razor pages from a Razor Class Library (RCL) into a modular Blazor Server application using dynamic assembly loading, so routes like /dashboard are recognized and do not show ‘Sorry, the content you are looking for does not exist’?

Project Structure

  • Main Blazor UI project (WebPortal.UI) with Routes.razor using AdditionalAssemblies from AssemblyScanning.GetAssemblies().
  • RCL module (WebPortal.Module.Dashboard) containing Pages/Dashboard.razor.
  • AssemblyScanning.cs dynamically loads assemblies starting with WebPortal.Module from the bin directory and filters those implementing IModule.

Key Code Snippets

Routes.razor:

razor
@using WebPortal.UI.Helpers
<Router AppAssembly="@typeof(Program).Assembly" NotFound="@typeof(Pages.NotFound)"
 AdditionalAssemblies="@AssemblyScanning.GetAssemblies().ToArray()">
 <!-- ... -->
</Router>

Program.cs:

csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
// ...
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();

Issue: No build or runtime errors, app runs, but /dashboard returns not found despite correct setup.

To make routes like /dashboard from your WebPortal.Module.Dashboard Razor Class Library (RCL) work in a modular Blazor Server app, add the dynamically loaded assemblies from AssemblyScanning.GetAssemblies() to both the Router’s AdditionalAssemblies in Routes.razor and MapRazorComponents.AddAdditionalAssemblies() in Program.cs. This dual registration ensures Blazor routing discovers @page "/dashboard" directives in RCL Blazor pages at runtime, fixing the “Sorry, there’s nothing at this address” 404 without build errors. Your current setup scans correctly but misses endpoint mapping in the app pipeline—it’s a common gotcha after .NET 8 upgrades.


Contents


Understanding Blazor Server Routing and Blazor Pages from Razor Class Libraries

Blazor Server apps handle routing through a Router component that scans assemblies for @page directives, generating RouteAttribute metadata for paths like /dashboard. When you pull Blazor pages from an RCL like WebPortal.Module.Dashboard, the host app (WebPortal.UI) won’t automatically know about them—especially in modular setups with dynamic loading.

Think about it: your Pages/Dashboard.razor has @page "/dashboard", but without explicit assembly references, the router treats it like a ghost. Blazor routing relies on two layers: client-side discovery via Router AdditionalAssemblies for interactive navigation, and server-side endpoint mapping via MapRazorComponents for HTTP requests. Skip either, and you get that frustrating not-found page. According to the official Blazor routing docs, RCL integration demands adding these assemblies to both spots for full coverage.

Why does this trip people up? Static references work fine in simple apps, but your bin-scanning approach for WebPortal.Module.* modules loads them at runtime. No errors during build—just silence at /dashboard.


Setting Up Blazor Razor Components in RCL for Modular Apps

Start in your RCL. Ensure WebPortal.Module.Dashboard is a Razor Class Library project targeting .NET 8+. Add Pages/Dashboard.razor with a proper @page directive:

razor
@page "/dashboard"
@rendermode InteractiveServer

<PageTitle>Dashboard</PageTitle>

<h1>Dashboard Loaded!</h1>
<p>This proves Blazor pages from RCL work with routing.</p>

Don’t forget the @rendermode if you’re using interactive server rendering—it’s picky in .NET 8 Blazor Server. Your RCL needs <Project Sdk="Microsoft.NET.Sdk.Razor"> in the .csproj, plus any shared _Imports.razor for @using directives.

Assets like CSS or JS? Drop them in wwwroot—they’ll serve via /_content/WebPortal.Module.Dashboard/. The Blazor class libraries guide confirms this setup makes components discoverable, but routing still needs host config. Pro tip: Implement IModule in a class like DashboardModule : IModule for your scanning filter. It’ll validate loaded assemblies later.

Quick test: Build the RCL, drop the DLL in bin, and reference it statically in WebPortal.UI. If /dashboard works, your RCL is solid—the issue is dynamic loading.


Dynamic Assembly Loading with AssemblyScanning for Blazor Route Discovery

Your AssemblyScanning.cs is on the right track. Here’s a refined version that scans bin/Debug/net8.0 (or bin/ in prod), loads WebPortal.Module.* DLLs, and filters IModule implementers:

csharp
using System.Reflection;

public static class AssemblyScanning
{
 public static IEnumerable<Assembly> GetAssemblies()
 {
 var binPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
 var moduleAssemblies = Directory
 .GetFiles(binPath, "WebPortal.Module.*.dll", SearchOption.AllDirectories)
 .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath)
 .Where(asm => asm.GetTypes().Any(t => t.IsAssignableTo(typeof(IModule))))
 .ToArray();

 return moduleAssemblies.Concat(new[] { typeof(Program).Assembly }); // Include host too
 }
}

This grabs your dashboard module dynamically. But scanning alone doesn’t register routes—Blazor needs the assemblies fed to the router and pipeline. A Stack Overflow thread on .NET 8 RCL routing nails it: runtime loading works, but forget the mappings, and 404s persist.

Load early? Call this in Program.cs startup or a scoped service if needed. Handles upgrades from .NET 7 where endpoint discovery changed.


Configuring Router AdditionalAssemblies in Routes.razor

Your Routes.razor snippet is close. Tweak it to ensure scanning happens before render:

razor
@using WebPortal.UI.Helpers
@inject IWebAssemblyHostEnvironment? HostingEnvironment

<Router AppAssembly="@typeof(Program).Assembly" 
 NotFound="@typeof(Pages.NotFound)"
 AdditionalAssemblies="@(AssemblyScanning.GetAssemblies().ToArray())">
 <Found Context="routeData">
 <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
 <FocusOnNavigate RouteData="@routeData" Selector="h1" />
 </Found>
</Router>

Injecting the environment helps in some edge cases, but the key is ToArray() for the immutable list. This tells the client-side router to scan RCLs for RouteAttribute on /dashboard. Test by navigating via link—should render interactively.

Still 404 on direct URL? That’s server-side. Next up: Program.cs.


Registering RCL Assemblies in Program.cs with MapRazorComponents

Here’s the missing piece. Update Program.cs to mirror the router:

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorComponents()
 .AddInteractiveServerComponents();

var app = builder.Build();

// Load assemblies early
var moduleAssemblies = AssemblyScanning.GetAssemblies().ToArray();

app.MapRazorComponents<App>()
 .AddInteractiveServerRenderMode()
 .AddAdditionalAssemblies(moduleAssemblies); // Critical line!

app.Run();

Chain .AddAdditionalAssemblies(moduleAssemblies) on MapRazorComponents—it maps RCL endpoints like /dashboard for HTTP. Without it, SignalR handles interactive bits, but direct fetches fail. The .NET 9 routing fallback docs stress this for modular Blazor Server.

Rebuild, hit /dashboard. Boom—dashboard loads.


Troubleshooting Blazor Routing 404 Errors in .NET 8+ Blazor Server

Not working yet? Check these:

  • Assembly load fails? Log AssemblyScanning.GetAssemblies()—ensure DLLs are in bin and IModule implemented.
  • Render mode mismatch? Add @rendermode InteractiveServer to RCL pages.
  • .NET 8 upgrade quirks? Clear bin/obj, verify <ImplicitUsings>enable</ImplicitUsings> in projects.
  • Static files? RCL wwwroot serves fine, but confirm no route conflicts.
  • Debug trick: Add logging: builder.Logging.AddConsole(); and trace IRazorComponentsServiceProvider.

Common pitfall: Scanning too late. Make GetAssemblies() static and call once. If SSR fallback kicks in, force interactive via AddInteractiveServerRenderMode().AddAdditionalAssemblies(...).


Best Practices for Blazor Components and Modular Blazor Server Apps

  • Scan once: Cache assemblies in a singleton service.
  • Version modules: Use semantic versioning for WebPortal.Module.*.
  • Lazy load: For heavy modules, defer scanning until first nav.
  • Security: Validate loaded types before registration.
  • Testing: Mock AssemblyScanning for unit tests.

Modular Blazor shines here—scale to dozens of RCLs without monolith bloat. Keep namespaces clean with @using WebPortal.Module.Dashboard in host _Imports.razor.


Sources

  1. Blazor Routing Fundamentals — Official guide on Router AdditionalAssemblies and RCL route discovery: https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-8.0
  2. Blazor .NET 8 RCL Routing Issue — Stack Overflow solution for 404s with MapRazorComponents chaining: https://stackoverflow.com/questions/77836160/blazor-in-net-8-routing-issue-when-load-page-from-razor-class-library
  3. Blazor Components in Class Libraries — Documentation on RCL setup, assets, and integration: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/class-libraries?view=aspnetcore-8.0
  4. Blazor Routing .NET 9 — Advanced routing with dynamic assemblies and render modes: https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-9.0&viewFallbackFrom=aspnetcore-8.0

Conclusion

Dual registration—Router plus MapRazorComponents—is the unlock for Blazor Server routing with dynamic RCLs like your dashboard module. Implement the code tweaks, scan reliably, and /dashboard will light up without 404s. Scale modularly, test rigorously, and you’ve got a flexible Blazor app ready for growth. If quirks persist, log your assemblies—it’s usually a load timing hiccup.

Microsoft Learn / Documentation Portal

To enable Blazor routing for pages from a Razor Class Library (RCL) in a modular Blazor Server app, dynamically load the RCL assembly before router creation and add it to AdditionalAssemblies in Routes.razor and MapRazorComponents.AddAdditionalAssemblies in Program.cs.

Use AssemblyScanning.GetAssemblies() to scan the bin directory for WebPortal.Module assemblies implementing IModule, then pass the array to both:

csharp
app.MapRazorComponents<App>().AddInteractiveServerRenderMode().AddAdditionalAssemblies(assemblies);

The @page "/dashboard" directive in Dashboard.razor generates RouteAttribute, which the router scans from AdditionalAssemblies, resolving routes without 404 errors.

H

In Blazor .NET 8 Server apps, routing fails for pages from RCL (e.g., 404 on routes) if not registered in Program.cs.

Fix by chaining AddAdditionalAssemblies on MapRazorComponents:

csharp
app.MapRazorComponents<App>()
 .AddAdditionalAssemblies(typeof(Dashboard).Assembly)
 .AddInteractiveServerRenderMode();

Combine with Router AdditionalAssemblies in Routes.razor for full route discovery. This resolves post-.NET 7 upgrade issues where endpoint mapping misses RCL Blazor components.

Microsoft Learn / Documentation Portal

Blazor components from RCL are consumable via full type names or @using directives in the host app.

Static assets in RCL wwwroot serve via _content/{PackageId}, and CSS isolation auto-applies.

For routing and dynamic modular loading, ensure RCL assemblies are added to Router AdditionalAssemblies and Program.cs mappings to discover @page routes like /dashboard in Blazor Server setups.

Microsoft Learn / Documentation Portal

For Blazor pages in RCL to work with routing in modular Blazor Server, include the assembly in Router AdditionalAssemblies (e.g., via AssemblyScanning for WebPortal.Module.*) and MapRazorComponents.AddAdditionalAssemblies.

RCL must have @page directives for RouteAttribute generation.

Dynamic bin scanning with Assembly.LoadFrom ensures runtime discovery, preventing NotFound on /dashboard; also add client assembly if Routes.razor needs interactive render modes.

Authors
H
Full Stack Developer
Sources
Microsoft Learn / Documentation Portal
Documentation Portal
Stack Overflow / Q&A Platform
Q&A Platform
Verified by moderation
NeuroAnswers
Moderation
Integrate RCL Pages in Blazor Server with Dynamic Loading