blazor-mudblazor-starter
Live application available Try it live →

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.

Live demo


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:

  • MudThemeProvider with bindable dark mode toggle, persisted to localStorage
  • MudAppBar with drawer toggle button, app title, dark mode switch, and overflow menu
  • MudDrawer with MudNavMenu containing links to Home, Counter, and Weather pages
  • MudPopoverProvider, MudDialogProvider, and MudSnackbarProvider for MudBlazor services
  • Responsive breakpoint detection: displays a MudToggleIconButton on small screens and a MudSwitch on larger screens
  • All UI state (dark mode, drawer open, screen size) persisted to localStorage and restored on first render

Key behavior:

  • Subscribes to IBrowserViewportService for breakpoint change notifications
  • Implements IAsyncDisposable to 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:

  • MudDataGrid with 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 AddWeather dialog via IDialogService, appends new entry to the collection
  • Edit: Opens EditWeather dialog with the selected item, replaces the entry in-place
  • Remove: Opens RemoveWeather confirmation 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.UnhandledExceptionHandler routes 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 symbols
  • DebuggerSupport -- disabled
  • InvariantGlobalization -- uses invariant culture
  • EventSourceSupport -- disabled
  • HttpActivityPropagationSupport -- disabled
  • MetadataUpdaterSupport -- disabled
  • StackTraceSupport -- disabled
  • UseSystemResourceKeys -- 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:

  1. 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).

  2. container-test: Builds a Docker image, runs the container on port 5030, and polls the /healthz endpoint 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:

  1. setup-build-test: Restores and builds with production settings (TRIM=true, EXTRA_OPTIMIZE=true, BUILD_CONFIGURATION=Release).

  2. 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 to ghcr.io/jonathanperis/blazor-mudblazor-starter:latest.

  3. deploy-image-azure: Deploys the GHCR image to Azure Web App using the azure/webapps-deploy action with a publish profile stored in AZURE_WEBAPP_PUBLISH_PROFILE secret.

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_PROFILE secret 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

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.