Blazor tutorial

Razor class library components wey get parameters, ChildContent render fragments, attribute splatting, and one bUnit test for each component.

Wetin you go build

Small contact form — input wey get label, submit button, @bind-Value binding, real keyboard and screen-reader behaviour — first bare, then styled, for about 10 minutes.

Before you start

You need git and di .NET SDK 8 or newer. Any Blazor project dey work — Blazor Web App, Server, or WebAssembly.

Step 1 — Get di code

git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-blazor-headless
dotnet build
dotnet test      # bUnit — 1,400+ cases across the catalog

Di components dey live inside src/LilyBlazorHeadless/Components/ — Button.razor, TextInput.razor — one Razor file each. Reference di project (or copy di files) from your Blazor app and add @using LilyBlazorHeadless.Components to _Imports.razor.

Step 2 — Your first component

<Button OnClick="Save">Save</Button>

E dey render <button class="button">. Di shared parameter conventions: Label (an aria-label override), CssClass (dem append am to di base hook), ChildContent, and [Parameter(CaptureUnmatchedValues = true)] attribute splatting so any extra HTML attribute go pass through.

Step 3 — Compose small form

<Form OnSubmit="HandleSubmit">
  <Field Label="Full name" Required="true">
    <TextInput Id="name" Label="Full name" @bind-Value="name" Required="true" />
  </Field>
  <Button Type="submit">Save</Button>
</Form>

@code {
  private string name = "";
}

Field dey take di visible Label as im own parameter instead of nested label component — pass am once, on Field, and TextInputfor Label dey provide di accessible name.

Verified 2026-08-29: earlier draft nest separate Label component inside Field, wey render two labels (one empty) because Field already dey render im own from di Label parameter — dem catch am by checking di component source; Form no get im own Label parameter, so di earlier draft usage of <Form> dem correct am to comot one instead of to invent attribute wey no exist. Di contact-form composed route na di same pattern, wey real e2e and axe tests don exercise.

Check your work: run di app and di form go look plain — exactly as dem plan am: di styling still dey come. Press Tab: focus go move through every control, di label go dey announced, and submit dey work. Di behaviour don finish; di look na yours and e be next.

Step 4 — Style am

Style di class hooks — .button, .text-input, .label, .field — inside app.css, or link ready-made theme inside App.razor / index.html:

<link rel="stylesheet" href="themes/wireframe.css" />

Step 5 — See everything dey work

Di Blazor Web example app show di full catalog styled and demoed, with Playwright e2e tests:

git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-blazor-web-examples
dotnet run --project src/LilyBlazorWebExamples

Next steps

  • Theming — runtime switching with di Blazor theme-picker helper (Razor class library for NuGet).
  • Preference helpers — language and text size.
  • Component catalog — props, ARIA, and keyboard for each component.

You get questions as you dey go? Di help page full of answers — and if any step fit clearer, tell us and we go gladly improve di tutorial.