MudBlazor: Material Design component library for Blazor (Server/WebAssembly)
- Stack: .NET (8.0/9.0/10.0), SCSS, minimal JS in TScripts/, bUnit tests
- Scale: ~450 components, ~3,700+ tests
- .NET 10.0 SDK (10.0.100+):
dotnet --version
CRITICAL: Target specific projects only. Solution-wide commands are too slow.
- Components:
src/MudBlazor/MudBlazor.csproj+src/MudBlazor.UnitTests/MudBlazor.UnitTests.csproj - Docs:
src/MudBlazor.Docs.Compiler/MudBlazor.Docs.Compiler.csproj+src/MudBlazor.Docs/MudBlazor.Docs.csproj - Analyzers:
src/MudBlazor.Analyzers/MudBlazor.Analyzers.csprojorsrc/MudBlazor.SourceGenerator/MudBlazor.SourceGenerator.csproj
# Clean (when build fails)
dotnet clean <project.csproj>
# Build
dotnet build <project.csproj> -c Release --nologo
# Test (ALWAYS use --filter and --no-build)
dotnet test src/MudBlazor.UnitTests/MudBlazor.UnitTests.csproj --filter "FullyQualifiedName~MudButton" --no-build -c Release --nologo# Format specific files/directories (use --include)
dotnet format <project.csproj> --include <path/to/changed/files>CI will fail if code is not formatted.
# Run docs locally
dotnet run --project src/MudBlazor.Docs.Server/MudBlazor.Docs.Server.csproj
# Run test viewer
dotnet run --project src/MudBlazor.UnitTests.Viewer/MudBlazor.UnitTests.Viewer.csprojsrc/
├── MudBlazor/ # Core library
│ ├── Components/ # Blazor components
│ ├── Styles/ # SCSS files
│ └── TScripts/ # JavaScript interop
├── MudBlazor.Docs/ # Documentation site
├── MudBlazor.Docs.Compiler/ # Auto-generates docs
├── MudBlazor.UnitTests/ # bUnit tests
├── MudBlazor.UnitTests.Viewer/ # Visual test runner
├── MudBlazor.Analyzers/ # Roslyn analyzers
└── MudBlazor.SourceGenerator/ # Source generators
Key config files:
src/.editorconfig- Code style rulessrc/Directory.Build.props- MSBuild properties.github/workflows/build-test-mudblazor.yml- CI/CD
- Never save
Find()orFindAll()results - elements become stale after re-render - Always use
InvokeAsync()for parameter changes/method calls
// GOOD
var comp = ctx.RenderComponent<MudTextField<string>>();
comp.Find("input").Change("Garfield"); // Query each time
comp.Find("input").Blur();
// BAD
var input = comp.Find("input"); // Becomes stale
input.Change("Garfield");
input.Blur(); // Will fail- Test components:
src/MudBlazor.UnitTests.Viewer/TestComponents/<ComponentName>/ - Tests:
src/MudBlazor.UnitTests/Components/<ComponentName>Tests.cs
Never put logic in parameter getters/setters. Use ParameterState framework.
// GOOD
private readonly ParameterState<bool> _expandedState;
[Parameter]
public bool Expanded { get; set; } // Auto-property only
public MudCollapse()
{
using var registerScope = CreateRegisterScope();
_expandedState = registerScope.RegisterParameter<bool>(nameof(Expanded))
.WithParameter(() => Expanded)
.WithEventCallback(() => ExpandedChanged)
.WithChangeHandler(OnExpandedChangedAsync);
}
private async Task OnExpandedChangedAsync()
{
if (_isRendered)
{
_state = _expandedState.Value ? CollapseState.Entering : CollapseState.Exiting;
await UpdateHeightAsync();
_updateHeight = true;
}
await ExpandedChanged.InvokeAsync(_expandedState.Value);
}
// BAD
private bool _expanded;
[Parameter]
public bool Expanded
{
get => _expanded;
set
{
if (_expanded == value) return;
_expanded = value;
_ = UpdateHeight(); // FORBIDDEN - unobserved async discard
_ = ExpandedChanged.InvokeAsync(_expanded);
}
}- Never overwrite parameters directly
- Never set external component parameters (BL0005)
- Use declarative binding instead
- RTL support:
[CascadingParameter] public bool RightToLeft { get; set; } - XML documentation for all public properties
- Unit tests for components with logic
- Use
CssBuilderfor classes and styles - Use CSS variables (no hard-coded colors)
- Documentation page:
src/MudBlazor.Docs/Pages/Components/<ComponentName>.razor
Naming:
- Instance/static fields:
_camelCase - Constants/public members:
PascalCase - Async methods:
Asyncsuffix
Critical Rules:
- CS4014: No unobserved async discards (
_ = SomeAsync()is ERROR) - BL0007: Parameter auto-properties (suggestion)
- File header required (copyright notice)
- Max 7 parameters, complexity ≤15, ≤4 returns per function