Home
blazor-mudblazor-starter
Blazor Server starter template with MudBlazor Material Design components. Built on .NET 9 with Docker multi-arch support and a CI/CD pipeline for GitHub Container Registry and Azure Web App deployment.
Quick Links
| Page | Description |
|---|---|
| Getting Started | Prerequisites, local run, Docker run |
| Project Structure | Directory layout and file descriptions |
| Components | Blazor components reference |
| Configuration | App settings, build flags, environment variables |
| Deployment | Docker, CI/CD, and Azure deployment |
Key Features
- Pre-configured MudBlazor layout with app bar, navigation drawer, breadcrumbs, and dark mode toggle
- Demo pages: Home, Counter, Weather (virtualized data grid with Add/Edit/Remove dialogs)
- Multi-architecture Docker image (AMD64 + ARM64) with health check endpoint at
/healthz - Production-optimized builds with AOT, ReadyToRun, and trimming support
- CI/CD pipeline: PR build checks with container health verification, main branch release to GHCR and Azure
- Responsive design with breakpoint-aware UI adaptation
- Right-click context menu with clipboard copy for data grid rows
Components
Layout Components
MainLayout.razor
The root layout component that provides the application shell. Inherits from LayoutComponentBase and implements IBrowserViewportObserver for responsive design.
Features:
MudThemeProviderwith bindable dark mode toggle, persisted to localStorageMudAppBarwith drawer toggle button, app title, dark mode switch, and overflow menuMudDrawerwithMudNavMenucontaining links to Home, Counter, and Weather pagesMudPopoverProvider,MudDialogProvider, andMudSnackbarProviderfor MudBlazor services- Responsive breakpoint detection: displays a
MudToggleIconButtonon small screens and aMudSwitchon larger screens - All UI state (dark mode, drawer open, screen size) persisted to localStorage and restored on first render
Key behavior:
- Subscribes to
IBrowserViewportServicefor breakpoint change notifications - Implements
IAsyncDisposableto unsubscribe from viewport events - Delays rendering until localStorage state is loaded to prevent flash of unstyled content
Breadcrumb.razor
A reusable breadcrumb navigation component that wraps MudBreadcrumbs in a MudCard.
Parameters:
| Parameter | Type | Description |
|---|---|---|
Items |
List<BreadcrumbItem> |
List of breadcrumb items to display |
Uses a custom separator template with MudIcon (arrow forward icon). Each page defines its own breadcrumb items and passes them to this component.
Page Components
Home.razor
Route: /
Landing page that displays a welcome card with a link to the MudBlazor documentation. Uses the Breadcrumb component with a single "Home" item.
Counter.razor
Route: /counter
Interactive counter demonstration. Displays the current count in a MudCard with a "Click me" MudButton that increments the value. Demonstrates Blazor's reactive data binding with a simple _currentCount field and IncrementCount method.
Weather.razor
Route: /weather
Full-featured data grid page demonstrating CRUD operations, virtualization, and clipboard integration.
Features:
MudDataGridwith 69,420 generated weather forecast entries- Multi-column display: Id, Date, Temperature (C/F), Summary (with duplicate columns for horizontal scroll demo)
- Multi-selection support with
SelectColumn - Quick filter search across all displayed columns
- Sortable and filterable columns with
SortMode.Multiple - Virtualized rendering for performance with large datasets
- Fixed header with configurable page sizes (10, 25, 50, 100, 500, 1000, 5000)
- Loading state with simulated 2-second delay
CRUD Operations:
- Add: Opens
AddWeatherdialog viaIDialogService, appends new entry to the collection - Edit: Opens
EditWeatherdialog with the selected item, replaces the entry in-place - Remove: Opens
RemoveWeatherconfirmation dialog, removes all selected items
Context Menu:
- Right-click on a row to copy a single line or all selected lines to the clipboard
- Clipboard data formatted as semicolon-separated values
Data model (WeatherForecast): Defined as a nested class with Id (Guid), Date (DateTime), TemperatureC (int), Summary (string?), and computed TemperatureF.
Error.razor
Route: /Error
Error page that displays when an unhandled exception occurs. Shows the request ID from Activity.Current or HttpContext.TraceIdentifier when available. Includes guidance about the Development environment.
Weather Dialog Components
AddWeather.razor
A MudDialog wrapped in an EditForm with DataAnnotationsValidator. Provides text fields for Weather ID (Guid), Date, Temperature (C), and Summary. On valid submission, returns the new WeatherForecast via DialogResult.Ok and shows a success snackbar notification.
EditWeather.razor
A MudDialog wrapped in an EditForm for editing an existing weather entry.
Parameters:
| Parameter | Type | Description |
|---|---|---|
Item |
Weather.WeatherForecast |
The weather entry to edit |
The Weather ID field is read-only. On valid submission, returns the edited item via DialogResult.Ok and shows a success snackbar notification.
RemoveWeather.razor
A simple MudDialog confirmation prompt. Displays a warning message asking the user to confirm deletion. On confirmation, returns DialogResult.Ok(true) and shows a success snackbar notification. Does not use EditForm since no data input is required.
MudBlazor Components Used
| Component | Usage |
|---|---|
MudLayout, MudAppBar, MudDrawer, MudMainContent |
Application shell structure |
MudThemeProvider |
Material Design theming with dark mode |
MudNavMenu, MudNavLink |
Side navigation |
MudBreadcrumbs |
Page navigation breadcrumbs |
MudDataGrid, PropertyColumn, SelectColumn, TemplateColumn |
Weather data table |
MudDataGridPager |
Data grid pagination |
MudDialog, MudDialogProvider |
Modal dialogs for CRUD operations |
MudSnackbar, MudSnackbarProvider |
Toast notifications |
MudButton, MudIconButton, MudToggleIconButton |
Action buttons |
MudTextField |
Form inputs and search |
MudCard, MudCardHeader, MudCardContent, MudCardActions |
Content cards |
MudMenu, MudMenuItem |
Context menu and overflow menu |
MudSwitch |
Dark mode toggle (large screens) |
MudText, MudLink, MudSpacer, MudDivider, MudIcon |
Typography and layout utilities |
MudPopoverProvider |
Popover rendering |
Configuration
Application Settings
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
| Key | Default | Description |
|---|---|---|
Logging:LogLevel:Default |
Information |
Minimum log level for all categories |
Logging:LogLevel:Microsoft.AspNetCore |
Warning |
Log level for ASP.NET Core framework logs |
AllowedHosts |
* |
Allowed host headers (all hosts by default) |
appsettings.Development.json
Overrides for local development. Currently mirrors the base logging configuration.
Environment Variables
| Variable | Default | Description |
|---|---|---|
ASPNETCORE_ENVIRONMENT |
Production |
Set to Development for local dev (auto-set by launch profiles) |
ASPNETCORE_URLS |
http://+:5000 |
Listening URL (set in Dockerfile for container builds) |
.NET SDK Version
Pinned in global.json:
{
"sdk": {
"version": "9.0.202",
"rollForward": "minor"
}
}
The rollForward: minor policy allows using any 9.0.x SDK version at or above 9.0.202.
MudBlazor Service Registration
In Program.cs, MudBlazor is registered with:
builder.Services.AddMudServices();
builder.Services.AddMudTranslations();
MudGlobal.UnhandledExceptionHandler = Console.WriteLine;
AddMudServices()registers all MudBlazor services (dialogs, snackbar, scroll, resize, etc.)AddMudTranslations()registers localization support (MudBlazor.Translations 3.3.0)MudGlobal.UnhandledExceptionHandlerroutes unhandled exceptions to the console
Docker Build Arguments
The Dockerfile accepts four build arguments that control the compilation output:
| Argument | Default | Values | Description |
|---|---|---|---|
AOT |
false |
true, false |
Enable Ahead-of-Time compilation for faster startup |
TRIM |
false |
true, false |
Enable ReadyToRun, single-file publish, and self-contained deployment |
EXTRA_OPTIMIZE |
false |
true, false |
Strip symbols, disable debugger support, invariant globalization |
BUILD_CONFIGURATION |
Debug |
Debug, Release |
.NET build configuration |
Build Flag Details
AOT (AOT=true): Enables PublishAot with OptimizationPreference set to Speed. Produces a native binary with faster cold-start performance.
TRIM (Trim=true): Enables PublishReadyToRun, PublishReadyToRunComposite, PublishSingleFile, and SelfContained. Produces an optimized single-file deployment.
EXTRA_OPTIMIZE (ExtraOptimize=true): Applies aggressive optimizations for minimal binary size:
TrimmerRemoveSymbols-- removes debug symbolsDebuggerSupport-- disabledInvariantGlobalization-- uses invariant cultureEventSourceSupport-- disabledHttpActivityPropagationSupport-- disabledMetadataUpdaterSupport-- disabledStackTraceSupport-- disabledUseSystemResourceKeys-- uses system resource keys instead of embedded strings
Production Defaults
The main-release.yml pipeline uses these defaults:
AOT=false
TRIM=true
EXTRA_OPTIMIZE=true
BUILD_CONFIGURATION=Release
Deployment
Docker
Build the Image
The Dockerfile uses a multi-stage build with the .NET 9 SDK and ASP.NET runtime images. The build context is the src/ directory.
docker build -t blazor-mudblazor -f src/WebClient/Dockerfile src/
With production optimizations:
docker build \
--build-arg AOT=false \
--build-arg TRIM=true \
--build-arg EXTRA_OPTIMIZE=true \
--build-arg BUILD_CONFIGURATION=Release \
-t blazor-mudblazor -f src/WebClient/Dockerfile src/
Run the Container
docker run -p 5000:5000 blazor-mudblazor
The container listens on port 5000 (ASPNETCORE_URLS=http://+:5000). The entry point is the compiled ./WebClient binary.
Multi-Architecture Support
The release pipeline builds for both linux/amd64 and linux/arm64/v8 using Docker Buildx with QEMU emulation. The Dockerfile installs clang and zlib1g-dev in the SDK stage to support AOT compilation on both architectures.
Pre-built Image
The latest release image is available from GitHub Container Registry:
docker pull ghcr.io/jonathanperis/blazor-mudblazor-starter:latest
docker run -p 5000:5000 ghcr.io/jonathanperis/blazor-mudblazor-starter:latest
CI/CD Pipelines
build-check.yml (Pull Requests)
Triggered on pull requests to main. Runs two jobs:
setup-build-test: Sets up the .NET SDK from
global.json, restores dependencies, and builds the project with debug settings (AOT=false,TRIM=false,BUILD_CONFIGURATION=Debug).container-test: Builds a Docker image, runs the container on port 5030, and polls the
/healthzendpoint up to 20 times (5-second intervals) to verify the application starts correctly. Fails the pipeline if the health check does not return HTTP 200.
main-release.yml (Main Branch)
Triggered on push to main or manual dispatch. Runs three sequential jobs:
setup-build-test: Restores and builds with production settings (
TRIM=true,EXTRA_OPTIMIZE=true,BUILD_CONFIGURATION=Release).build-push-image: Sets up QEMU and Docker Buildx, authenticates to GitHub Container Registry, builds the multi-arch image (
linux/amd64,linux/arm64/v8), and pushes toghcr.io/jonathanperis/blazor-mudblazor-starter:latest.deploy-image-azure: Deploys the GHCR image to Azure Web App using the
azure/webapps-deployaction with a publish profile stored inAZURE_WEBAPP_PUBLISH_PROFILEsecret.
codeql.yml
Runs CodeQL security analysis on the codebase.
deploy.yml (GitHub Pages)
Triggered on push to main or manual dispatch. Deploys the static docs/ directory to GitHub Pages using actions/configure-pages, actions/upload-pages-artifact, and actions/deploy-pages.
Azure Web App
The application is deployed to Azure App Service in the Brazil South region. The deployment uses a container image from GHCR, configured via the Azure Web App publish profile.
Live demo: blazor-mudblazor-starter
Azure Deployment Requirements
- An Azure Web App configured for Linux container deployment
- The
AZURE_WEBAPP_PUBLISH_PROFILEsecret set in the GitHub repository settings (download from Azure Portal > Web App > Deployment Center > Manage publish profile) - GHCR image access configured on the Azure Web App (the image is public via GitHub Packages)
Getting Started
Prerequisites
- .NET 9 SDK (9.0.202 or later)
- Docker (optional, for container builds)
Run Locally
git clone https://github.com/jonathanperis/blazor-mudblazor-starter.git
cd blazor-mudblazor-starter
dotnet restore
dotnet run --project src/WebClient
Open http://localhost:5000 in your browser.
The https launch profile is also available at https://localhost:5001.
Run with Docker
Build the image from the src/ context using the Dockerfile inside src/WebClient/:
docker build -t blazor-mudblazor -f src/WebClient/Dockerfile src/
docker run -p 5000:5000 blazor-mudblazor
Open http://localhost:5000 in your browser.
Docker Build Arguments
You can pass build arguments to control optimization:
docker build \
--build-arg AOT=false \
--build-arg TRIM=true \
--build-arg EXTRA_OPTIMIZE=true \
--build-arg BUILD_CONFIGURATION=Release \
-t blazor-mudblazor -f src/WebClient/Dockerfile src/
See Configuration for details on each build argument.
Access URLs
| Context | URL |
|---|---|
| Local (HTTP) | http://localhost:5000 |
| Local (HTTPS) | https://localhost:5001 |
| Docker container | http://localhost:5000 |
| Live demo | blazor-mudblazor-starter |
Project Structure
blazor-mudblazor-starter/
├── .github/
│ ├── dependabot.yml # Dependabot configuration for dependency updates
│ └── workflows/
│ ├── build-check.yml # PR validation: .NET build + Docker build + health check
│ ├── codeql.yml # CodeQL security analysis
│ ├── deploy.yml # GitHub Pages deployment
│ └── main-release.yml # Release: optimized build + GHCR push + Azure deploy
├── src/
│ └── WebClient/
│ ├── Components/
│ │ ├── App.razor # Root HTML document with MudBlazor CSS/JS imports
│ │ ├── Routes.razor # Blazor router, defaults to MainLayout
│ │ ├── _Imports.razor # Global using directives for all components
│ │ ├── Layout/
│ │ │ ├── MainLayout.razor # App shell: app bar, drawer, dark mode, responsive breakpoints
│ │ │ └── Breadcrumb.razor # Reusable breadcrumb navigation component
│ │ ├── Pages/
│ │ │ ├── Home.razor # Landing page (route: /)
│ │ │ ├── Counter.razor # Interactive counter demo (route: /counter)
│ │ │ ├── Weather.razor # Virtualized data grid with CRUD (route: /weather)
│ │ │ └── Error.razor # Error page with request ID display (route: /Error)
│ │ └── Weather/
│ │ ├── AddWeather.razor # MudDialog for adding weather entries
│ │ ├── EditWeather.razor # MudDialog for editing weather entries
│ │ └── RemoveWeather.razor # MudDialog for delete confirmation
│ ├── Properties/
│ │ └── launchSettings.json # Local dev profiles (HTTP on 5000, HTTPS on 5001)
│ ├── wwwroot/ # Static assets (favicon, CSS)
│ ├── appsettings.json # Base configuration (logging, allowed hosts)
│ ├── appsettings.Development.json # Development logging overrides
│ ├── Dockerfile # Multi-stage .NET 9 build (AMD64 + ARM64)
│ ├── Program.cs # App entry point, MudBlazor service registration
│ └── WebClient.csproj # Project file: .NET 9, MudBlazor 9.2.0, AOT/Trim flags
├── .editorconfig # Code style settings
├── .gitignore # Git ignore rules
├── global.json # .NET SDK version pin (9.0.202, roll-forward: minor)
├── LICENSE # MIT license
├── README.md # Project overview and quick start
└── WebClient.sln # Solution file
Key Directories
src/WebClient/Components/Layout/
Contains the application shell. MainLayout.razor provides the MudBlazor layout with app bar, navigation drawer, dark mode toggle (persisted via localStorage), and responsive breakpoint handling. Breadcrumb.razor is a reusable component that accepts a list of BreadcrumbItem parameters.
src/WebClient/Components/Pages/
Contains routable page components. Each page uses the Breadcrumb component for navigation context. The Weather page demonstrates a full CRUD workflow with MudDataGrid, dialog services, snackbar notifications, and clipboard integration.
src/WebClient/Components/Weather/
Contains MudDialog components used by the Weather page for Add, Edit, and Remove operations. Each dialog uses EditForm with DataAnnotationsValidator for form validation (except RemoveWeather, which is a simple confirmation).
.github/workflows/
Contains four GitHub Actions workflows: build-check.yml for PR validation (includes container health check against /healthz), main-release.yml for production releases to GHCR and Azure, codeql.yml for security analysis, and deploy.yml for GitHub Pages deployment.