# Blazor From Zero

> Learn to build interactive web UIs in C# instead of JavaScript: components and Razor, the Server vs WebAssembly hosting models, data binding, events and the component lifecycle, forms and validation, component communication and state, and calling APIs with dependency injection. Microsoft's answer to single-page apps, taught mental-model-first.


---

# Blazor From Zero

Blazor lets you build interactive web front-ends in **C# instead of JavaScript**. If you live in the .NET
world, that's a genuinely big deal: the same language, types, and tooling you use on the server now drive
the browser UI too - components, data binding, events, forms, the lot. It's Microsoft's answer to the
single-page-app frameworks (React, Vue, Angular), and it shares their core idea: a UI built from reusable,
self-contained **components** that re-render when their data changes.

The mental model is one unit and one hosting choice. The unit is the **component** - a `.razor` file that
mixes HTML markup with C# logic (in an `@code` block) and re-renders when its **state** changes. The
choice is **where that C# runs**: **Blazor Server** runs it on the server and streams UI updates to the
browser over a live connection, while **Blazor WebAssembly** ships a .NET runtime to the browser and runs
the C# there. Same components, same code - different trade-offs (covered in Phase 1). Hold "the UI is a
tree of components that re-render on state change, and you pick where the C# executes," and Blazor clicks.

> 📝 This teaches the **framework** - it assumes you know **C#** (classes, `async`/`await`, events,
> generics - [C# From Zero](/guides/csharp-from-zero)) and basic **HTML/CSS**. It pairs with
> [ASP.NET Core](/guides/aspnet-core-from-zero) (which hosts it and serves the APIs it calls). The
> component model echoes the JS frameworks ([What a Framework Even Is](/guides/what-a-framework-even-is)).
> Blazor compiles and runs as a .NET app, so examples are shown with the commands to run them.

## How to read this

Read in order - it grows from a single component to a small **products** UI that talks to an API. Phases
carry difficulty badges.

## The phases

**Part 1 - Components (🟢 Basic → 🟡)**
1. **[What Blazor Is (Server vs WebAssembly)](01-what-blazor-is.md)** 🟢 - components, the two hosting models, and a running app.
2. **[Components & Razor](02-components-and-razor.md)** 🟡 - `.razor` files, markup + `@code`, and rendering.
3. **[Data Binding](03-data-binding.md)** 🟡 - `@bind`, one-way vs two-way, and reacting to state.

**Part 2 - Interactivity (🟡 → 🔴)**
4. **[Events & the Component Lifecycle](04-events-and-lifecycle.md)** 🟡 - `@onclick`, `OnInitialized`/`OnParametersSet`, and `StateHasChanged`.
5. **[Forms & Validation](05-forms-and-validation.md)** 🟡 - `EditForm`, data annotations, and validation messages.
6. **[Component Communication & State](06-communication-and-state.md)** 🔴 - `[Parameter]`, `EventCallback`, cascading values, and shared state.

**Part 3 - Real apps (🔴 → 🟢)**
7. **[Calling APIs & Dependency Injection](07-calling-apis-and-di.md)** 🔴 - `@inject`, `HttpClient`, and loading data from a backend.
8. **[Where to Go Next](08-where-to-go-next.md)** 🟢 - Blazor vs React/Angular, the render modes, and what to build.

> The throughline: a Blazor app is a **tree of components that re-render when their state changes**, and
> you choose **where the C# runs** (server or WebAssembly). Everything else is detail on those two ideas.


---

# What Blazor Is (Server vs WebAssembly)

You know [C#](/guides/csharp-from-zero) - classes, awaited tasks, events. For years there was an
invisible wall: when the work moved to the browser, you put C# down and picked up JavaScript. Two
languages, two ecosystems, two mental models for one app. **Blazor tears that wall down.** It lets you
build the front-end - buttons, forms, live-updating lists - in the same C# you already write on the
server.

That's the pitch in one line: Blazor is Microsoft's answer to React, Vue, and Angular, except in C#
instead of JavaScript. It's a [framework](/guides/what-a-framework-even-is) - it runs the show and
calls *your* code at the right moments - hosted by [ASP.NET Core](/guides/aspnet-core-from-zero), the
same engine that serves your APIs. Same language top to bottom, same types flowing from database to
button click.

## The one mental model to hold

Before any code, hold these two ideas - everything else in this guide hangs off them.

💡 **First: the UI is a tree of components that re-render when their state changes.** A component is a
self-contained chunk of UI - a counter, a product card, a whole page - that owns some data (its
*state*) and knows how to draw itself. When that data changes, the component redraws. You don't reach
into the page and manually update text; you change a variable and Blazor figures out what needs to
change on screen. If you've seen React or Vue, this is the same idea wearing C# clothes.

💡 **Second: you choose *where* the C# runs.** This is unique to Blazor, and it's the big decision of
the whole framework. Your component code can run on the **server** (and stream UI updates to the
browser) or inside the **browser itself** via WebAssembly. The component code is *identical* either
way - what changes is where the work happens and what trade-offs you accept.

Hold those: *tree of components that re-render on state change*, and *you pick where the C# executes*.

## Meet a component

Here's the "hello world" of Blazor - a counter. It lives in a file called `Counter.razor`. The
`.razor` extension is how you know you're looking at a component.

```razor
<h1>Counter</h1>
<p>Count: @count</p>
<button @onclick="Increment">Click me</button>

@code {
    private int count = 0;
    private void Increment() => count++;
}
```

*What just happened:* a component is two things stacked together - **markup** on top, a **`@code`
block** on the bottom. The top half looks like ordinary HTML, because it mostly is. The bottom half is
plain C#: a field `count` and a method `Increment`.

The magic is the `@`. Wherever you write `@count` in the markup, Blazor drops in the *current value* of
that C# field, so the page shows "Count: 0". `@onclick="Increment"` wires the browser's click event
straight to your C# method - no `addEventListener`, no JavaScript. When the user clicks, `Increment`
runs, `count` goes up by one, and - the key part - **because the state changed, Blazor re-renders the
component and the displayed number updates automatically.** You changed a variable; the framework did
the rest.

That loop - *state changes → component re-renders* - is the heartbeat of every Blazor app you'll
ever build.

## Where does that C# run? Server vs WebAssembly

Now the big decision. That `Counter` component has to execute its C# *somewhere*. Blazor gives you
two homes for it, and they trade off in opposite directions.

📝 **Blazor Server** - the component's C# runs **on the server**. When the user clicks the button, the
click travels over a live **SignalR** connection to the server, `Increment` runs there, and Blazor
streams just the tiny UI diff back to patch the page.

- ✅ Tiny initial download (the browser only gets a thin script, not a runtime). Full server access - 
  your component can touch the database or server-only secrets directly.
- ⚠️ Needs a **constant connection**. Every interaction is a round trip to the server, so there's
  latency per click, and the app stops working if the connection drops.

📝 **Blazor WebAssembly (WASM)** - a complete .NET runtime is shipped *to the browser*, and your
component's C# runs **client-side**, right there on the user's machine.

- ✅ No per-click server round trip - clicks are handled locally and feel instant. Works **offline**
  once loaded. The server is free to be a plain API.
- ⚠️ **Larger initial download** (you're shipping a .NET runtime). Runs inside the browser sandbox,
  so it can't reach the database directly - it calls an API like any other front-end.

Here's the mental picture of the two paths a click can take:

```mermaid
flowchart LR
  C[User clicks button] --> Q{Where does the C# run?}
  Q -->|Blazor Server| S[Server runs C#<br/>over SignalR] --> D[UI diff streamed back]
  Q -->|Blazor WebAssembly| B[Browser runs C#<br/>in WASM] --> L[UI updated locally]
```

*One idea:* same component, same click handler - the only difference is whether the C# executes on
the server (and streams the result back) or inside the browser. That's the entire Server-vs-WASM
distinction in one diagram.

📝 **The modern shape (.NET 8).** You no longer pick one model for the *whole* app up front. .NET 8
unified them into a single **Blazor Web App** project where you set a **render mode** *per component*:
`InteractiveServer` (server), `InteractiveWebAssembly` (browser), or `InteractiveAuto` (start on the
server for a fast first load, then switch to WebAssembly for later visits). There's also plain
**static server rendering (SSR)** for components that just display data. The crucial part: **the
component code you write is the same across all of them** - the render mode decides *where* it runs,
not *what* you write.

## Create and run your first app

Enough theory - let's get one running. The .NET SDK ships a template:

```bash
dotnet new blazor -o MyApp
cd MyApp
dotnet run
```

*What just happened:* `dotnet new blazor` scaffolded a complete Blazor Web App named `MyApp` - project
file, a few starter components (including a `Counter` much like the one above), and the ASP.NET Core
host that serves it. `dotnet run` compiled it and started a local web server; open the URL it prints
(something like `https://localhost:5001`) and you'll see a working app, counter and all. For the
browser-only flavor, `dotnet new blazorwasm` scaffolds a standalone WebAssembly project - but the
unified `blazor` template is the modern default, so start there.

⚠️ The first `dotnet run` can feel slow - it restores packages and compiles the whole project. That's
a one-time cost; later runs are quick, and the dev server reloads as you edit.

Throughout this guide we'll grow one running example: a small **products** UI. It starts as a simple
counter, then becomes a list of products that - by Phase 7 - loads its data from a real API. Build
along in your `MyApp` project and you'll have a working mini-app by the end, not just snippets.

## Recap

1. **Blazor builds interactive web UIs in C# instead of JavaScript** - it's Microsoft's answer to
   React/Vue/Angular, hosted by [ASP.NET Core](/guides/aspnet-core-from-zero).
2. **A Blazor app is a tree of components that re-render when their state changes.** You change a
   C# variable; the framework redraws what needs redrawing. You never patch the DOM by hand.
3. **A component is markup + a `@code` block** in a `.razor` file. `@count` renders a field's value;
   `@onclick="Method"` wires a browser event to your C#.
4. **The big choice is where the C# runs.** **Blazor Server** runs it on the server over a live
   SignalR connection (small download, full server access; needs a constant connection, latency per
   click). **Blazor WebAssembly** ships a .NET runtime to the browser and runs it client-side (works
   offline, no round trips; larger initial download, sandbox limits).
5. 📝 **.NET 8 unified both** into a Blazor Web App with per-component **render modes**
   (`InteractiveServer`, `InteractiveWebAssembly`, `InteractiveAuto`, plus static SSR) - and the
   component code is the *same* across all of them.
6. **Create with `dotnet new blazor`, run with `dotnet run`.** We'll grow one products UI across the
   guide, starting from a counter.

## Quick check

Three questions on the ideas that have to stick - what a component is, the re-render loop, and the
Server-vs-WebAssembly trade-off:

```quiz
[
  {
    "q": "What is the core mental model of a Blazor app?",
    "choices": [
      "A tree of components that re-render when their state changes; you choose where the C# runs",
      "A single HTML file that you manually update with JavaScript on every event",
      "A set of stored procedures that run only inside the database",
      "A collection of CSS files compiled into a desktop application"
    ],
    "answer": 0,
    "explain": "Blazor's UI is a tree of self-contained components. When a component's state (its C# data) changes, the framework re-renders it automatically. The hosting choice - Server or WebAssembly - decides where that C# executes."
  },
  {
    "q": "In Blazor Server, where does a component's C# run when the user clicks a button?",
    "choices": [
      "On the server; the click goes over a SignalR connection and the UI diff is streamed back to the browser",
      "Entirely in the browser via a downloaded .NET runtime, with no server involved",
      "In the database engine as a trigger",
      "It doesn't run anywhere - Blazor Server is static HTML only"
    ],
    "answer": 0,
    "explain": "Blazor Server keeps the C# on the server. The click travels over a live SignalR connection, your code runs server-side, and Blazor streams the small UI diff back to patch the page. That's why it needs a constant connection and has per-click latency."
  },
  {
    "q": "Compared to Blazor Server, what is the main trade-off of Blazor WebAssembly?",
    "choices": [
      "Larger initial download (it ships a .NET runtime) but no per-click server round trip and it works offline",
      "It requires writing the component in JavaScript instead of C#",
      "It cannot run any C# at all in the browser",
      "It has a smaller download but always needs a constant server connection"
    ],
    "answer": 0,
    "explain": "WebAssembly ships a .NET runtime to the browser, so the first load is larger. In return, the C# runs client-side: interactions are handled locally (no round trip) and the app keeps working offline once loaded. The component code itself is identical to Server."
  }
]
```


---

# Components & Razor

Phase 1 showed the big picture: a Blazor app is a tree of components, and you choose where the C# runs. Now we open up a single component and look inside - where Blazor stops being an abstract idea and becomes something you can actually type.

Here's the mental model to hold before touching any code: **a component is markup plus a `@code` block, compiled together into one C# class.** The HTML you write is the shape of the UI; the `@code` block is the data and behavior behind it; the `@` symbol is the bridge that lets one reach into the other. Once that clicks, every piece of Razor you see is a variation on "HTML, with C# woven in through `@`."

> 📝 A component lives in a **`.razor`** file, and the file name is the component name. `ProductCard.razor` defines a component you'll later use as `<ProductCard />`. PascalCase, always - the compiler turns the file name into a class name, and C# classes are PascalCase.

## The two halves of a component

Let's build the running example for this guide: a `ProductCard` that shows a single product. Start with the smallest version that has both halves - some markup, and a `@code` block holding the data it renders.

```razor
<div class="card">
    <h3>@product.Name</h3>
    <p>@product.Price.ToString("C")</p>
</div>

@code {
    private Product product = new() { Name = "Mechanical Keyboard", Price = 89.99m };

    private record Product
    {
        public string Name { get; init; } = "";
        public decimal Price { get; init; }
    }
}
```

*What just happened:* the top part is plain HTML except for `@product.Name` and `@product.Price...` - **Razor expressions**: `@` followed by C# that gets evaluated and dropped into the page. The `@code { }` block is ordinary C#: a field (`product`) and a little `record` type. When this component renders, Blazor runs the C#, evaluates each `@`, and produces the final HTML. Markup on top, code on the bottom, `@` stitching them together.

## Razor essentials

Everything in Razor flows from one symbol. `@` means "switch from HTML into C# here." How much C# follows depends on what comes after the `@`.

**`@expression` renders a value.** This is the most common case - `@` followed by a simple expression, and Razor writes the result into the HTML:

```razor
<p>@product.Name</p>
<p>You have @items.Count items.</p>
```

*What just happened:* `@product.Name` evaluates the property and renders the string; `@items.Count` does the same with an `int` - Razor calls `.ToString()` for you. The `@` reaches into C#, grabs the value, and the rest of the line stays HTML.

When the expression has spaces or operators that Razor might confuse with surrounding markup, wrap it in `@(...)` to be explicit:

```razor
<p>Total: @(product.Price * quantity)</p>
```

*What just happened:* the parentheses tell Razor exactly where the C# starts and stops - without them, it might try to read the `*` or the space as markup. When in doubt, `@(...)`.

**Control flow uses C# directly in the markup.** There's no special template language for loops and conditionals - you write real `@if`, `@foreach`, `@for`, `@switch`, and the markup inside the braces gets rendered each time through:

```razor
@if (product.InStock)
{
    <span class="badge">In stock</span>
}
else
{
    <span class="badge muted">Sold out</span>
}
```

*What just happened:* `@if` is a normal C# `if`, but the braces hold **markup** instead of statements - C# control flow that emits HTML.

Loops work the same way - here's the product **list** that will sit alongside `ProductCard`:

```razor
<ul class="product-list">
    @foreach (var p in products)
    {
        <li>@p.Name - @p.Price.ToString("C")</li>
    }
</ul>

@code {
    private List<Product> products = new()
    {
        new() { Name = "Mechanical Keyboard", Price = 89.99m },
        new() { Name = "USB-C Hub", Price = 34.50m },
        new() { Name = "Laptop Stand", Price = 42.00m },
    };
}
```

*What just happened:* `@foreach` loops over `products`, and the `<li>` inside the braces renders once per item - three products means three `<li>` elements. Exactly how you'd loop in C#, just emitting markup instead of writing to a console.

> 💡 One thing that trips people up: **how do you print a literal `@` sign?** An email address like `you@example.com` in your markup would make Razor think `@example` is C#. Escape it by doubling: `you@@example.com` renders a single `@`.

## Directives: pages vs reusable components

At the very top of a `.razor` file you'll often see lines starting with `@` that aren't expressions - they're **directives**. They configure the component itself rather than rendering anything.

The one you'll meet first is `@page`:

```razor
@page "/products"

<h1>Our Products</h1>

<ul class="product-list">
    @foreach (var p in products)
    {
        <li>@p.Name - @p.Price.ToString("C")</li>
    }
</ul>

@code {
    private List<Product> products = new() { /* ... */ };
}
```

*What just happened:* `@page "/products"` makes this component a **routable page** - navigate to `/products` and Blazor renders it. Without `@page`, a component isn't reachable by URL on its own.

> 📝 The key distinction: a component **with** `@page` is a *page* - it has a URL the router can land on. A component **without** `@page` is a *reusable piece* - `ProductCard` has no URL; you drop its tag inside another component (`<ProductCard />`). Pages are destinations; reusable components are the building blocks assembled inside them. We'll wire data into `<ProductCard Product="p" />` in Phase 6.

Two other directives you'll see soon (don't worry about the details yet):

```razor
@using MyApp.Models
@inject ProductService Products
```

*What just happened:* `@using` imports a namespace, exactly like a `using` at the top of a C# file - write `Product` instead of `MyApp.Models.Product`. `@inject` asks for a service via dependency injection, how a component gets things like an `HttpClient` to load data. Covered properly in [Phase 7: Calling APIs & Dependency Injection](07-calling-apis-and-di.md) - for now, recognize the shape.

## Composing components into a tree

A component becomes useful when other components *use* it. You render one by writing its name as an HTML tag. Here's a products page that uses `ProductCard`:

```razor
@page "/products"

<h1>Our Products</h1>

<div class="grid">
    <ProductCard />
    <ProductCard />
    <ProductCard />
</div>
```

*What just happened:* each `<ProductCard />` tells Blazor to render the `ProductCard` component right there. The page is a component; it contains three `ProductCard` components; each is its own markup-plus-`@code` unit. That nesting **is** the component tree from Phase 1 - a page at the top, smaller components inside it, all the way down. (These three cards show the same hardcoded product for now; passing each its own data is what Phase 6's `Product="p"` parameter does.)

## A note on what "compiled" really means

Something that saves real debugging time once internalized: **Razor is compiled, not interpreted.** Your markup and `@code` block aren't read line-by-line at runtime - they're compiled together into a regular C# class before the app ever runs.

> ⚠️ The practical consequence: **mistakes in Razor are compile errors, not runtime errors.** A mismatched `{ }`, an unclosed tag in a `@foreach`, or a stray `@` where you meant a literal - these stop the build with an error message, same as a typo in any C# file. The compiler catches a whole category of bugs before the page ever loads. When the build fails after editing a `.razor` file, read the error like any C# compile error - it's pointing at your markup.

## Recap

- A **component** is **markup + a `@code` block, compiled into one C# class**; `@` is the bridge from HTML into C#.
- `@expression` renders a value; use `@(...)` when the expression isn't a simple member access; double `@@` to print a literal `@`.
- Control flow is real C# in the markup - `@if`/`@else`, `@foreach`, `@for`, `@switch` - with HTML inside the braces.
- A component **with `@page`** is a routable **page** (it has a URL); a component **without `@page`** is a **reusable piece** you place as a tag, like `<ProductCard />`.
- You compose components by using them as tags; nested tags form the **component tree**.
- Razor **compiles** - brace, tag, and `@` mistakes are **compile errors**, caught at build time, not runtime.

## Quick check

Test the mental model before moving on:

```quiz
[
  {
    "q": "What does a component's @code block hold?",
    "choices": ["The CSS styles for the component", "The component's fields and methods (its C# logic)", "A list of other components to import", "The compiled HTML output"],
    "answer": 1,
    "explain": "The @code block is ordinary C# - fields, methods, and types - that compiles together with the markup into a single class."
  },
  {
    "q": "What is the difference between a component with @page and one without it?",
    "choices": ["@page makes it run on the server instead of WebAssembly", "@page makes it a routable page with a URL; without it, the component is used as a tag inside other components", "@page is required for every .razor file", "Without @page the component cannot have a @code block"],
    "answer": 1,
    "explain": "@page \"/products\" gives a component a URL the router can land on. Reusable components like ProductCard have no @page - you place them as tags such as <ProductCard />."
  },
  {
    "q": "You have a mismatched brace inside a @foreach loop in your .razor file. When does it surface?",
    "choices": ["At runtime, when the loop first executes", "As a compile error at build time", "Only when a user visits the page", "It is silently ignored and the loop is skipped"],
    "answer": 1,
    "explain": "Razor is compiled, not interpreted. Markup and @code compile into a C# class, so brace and @ mistakes are compile errors caught at build time."
  }
]
```


---

# Data Binding

Here's the one idea that makes Blazor feel alive instead of static: **binding ties your markup to your C# state.** You don't manually grab an element and shove text into it the way you would with `document.getElementById` in JavaScript. You declare a relationship - "this bit of UI reflects that field" - and Blazor keeps them in sync.

That relationship runs in one of two directions:

- **One-way** (`@field`): your C# state flows *into* the markup. When the field changes and the component re-renders, the DOM updates. The data travels state → UI.
- **Two-way** (`@bind`): your C# state and a form input stay locked together in *both* directions. The user types, the field updates; your code changes the field, the input updates. Data travels state ↔ UI.

Hold that picture - **one-way is a read-out, two-way is a handshake** - and everything below is just syntax for those two cases. We'll build it on the running **products** UI from the earlier phases.

> 📝 You already met one-way binding in [Components & Razor](02-components-and-razor.md) without us naming it. Every time you wrote `@something` in markup, that was one-way binding. This phase names it, then adds the two-way kind.

## One-way binding: state into markup

When you drop `@expression` into your markup, Blazor evaluates that C# and renders the result. If the value changes later (and the component re-renders), the rendered output changes with it. The data only ever flows one way: from your field into the page.

```razor
<h2>@ProductName</h2>
<p>Price: $@Price</p>
<p>In stock: @(InStock ? "yes" : "no")</p>

@code {
    private string ProductName = "Mechanical Keyboard";
    private decimal Price = 89.99m;
    private bool InStock = true;
}
```

*What just happened:* the three `@` expressions read straight out of the `@code` fields and render their current values. Notice `@(InStock ? ... : ...)` - when the expression is more than a simple name, wrap it in parentheses so Blazor knows where the C# ends and markup resumes. This is a one-way relationship: the markup mirrors the fields, but nothing in the markup can change them. For that, you need two-way binding.

## Two-way binding with `@bind`

A read-out isn't enough for a form. When the user types into a search box or edits a price, you want that typed value to land back in your C# field. That's `@bind`.

`@bind` ties a form element's value to a C# field in **both** directions: change the field in code and the input updates; type in the input and the field updates. Let's wire up a search box that filters the product list.

```razor
<input @bind="filter" placeholder="Search products..." />
<p>Filtering by: <strong>@filter</strong></p>

<ul>
    @foreach (var p in products.Where(p => p.Contains(filter, StringComparison.OrdinalIgnoreCase)))
    {
        <li>@p</li>
    }
</ul>

@code {
    private string filter = "";
    private List<string> products = new()
    {
        "Mechanical Keyboard", "Wireless Mouse", "USB-C Hub", "Monitor Arm"
    };
}
```

*What just happened:* `@bind="filter"` connects the text box to the `filter` field. When `filter` changes, the `@foreach` re-runs and the list narrows. The `<strong>@filter</strong>` next to it is *one-way* binding reading the same field - one field, two roles, side by side.

`@bind` isn't only for text boxes. It adapts to the element it's on:

```razor
<select @bind="category">
    <option value="all">All</option>
    <option value="input">Input devices</option>
    <option value="display">Displays</option>
</select>

<label>
    <input type="checkbox" @bind="inStockOnly" />
    In stock only
</label>

<p>Category: @category · In-stock filter: @inStockOnly</p>

@code {
    private string category = "all";
    private bool inStockOnly = false;
}
```

*What just happened:* on a `<select>`, `@bind` reads and writes the chosen `<option>`'s `value`. On `<input type="checkbox">`, it binds to a `bool` - checked is `true`, unchecked `false`. Blazor picks the right HTML attribute and conversion per element, so you write the same `@bind` everywhere.

> 💡 The timing detail that trips people up: by default `@bind` on a text input syncs on **`onchange`** - fires when the input *loses focus*, not on every keystroke. In the search example above, `filter` only updates after you click away or press Tab, often not what you want for a live search. The next section fixes it.

## Controlling *when* and *how* it syncs

`@bind` has a few companion directives that tune its behavior.

**`@bind:event`** changes the event that triggers the sync. Switch it to `oninput` and the field updates on every keystroke - perfect for a live filter:

```razor
<input @bind="filter" @bind:event="oninput" placeholder="Search products..." />
<p>Showing results for: <strong>@filter</strong></p>

@code {
    private string filter = "";
}
```

*What just happened:* with `@bind:event="oninput"`, `filter` updates as the user types each character, so the filtered list reacts instantly instead of waiting for the box to lose focus. The default `onchange` is fine for a price field you only care about once editing is done; `oninput` is for anything that should feel live.

**`@bind:format`** controls how a value is rendered into the input - most commonly for dates:

```razor
<input type="date" @bind="restockDate" @bind:format="yyyy-MM-dd" />
<p>Restocks on: @restockDate.ToShortDateString()</p>

@code {
    private DateTime restockDate = DateTime.Today;
}
```

*What just happened:* `@bind:format="yyyy-MM-dd"` tells Blazor how to format the `DateTime` when writing it into the input's value. Without a matching format, an `<input type="date">` may refuse to display the value, since the browser expects exactly `yyyy-MM-dd`.

**`@bind:after`** runs a method *after* the bound field has been updated - the right place to react to a change (recalculate, log, call an API):

```razor
<input @bind="filter" @bind:event="oninput" @bind:after="OnFilterChanged" />
<p>@status</p>

@code {
    private string filter = "";
    private string status = "";

    private void OnFilterChanged()
    {
        status = string.IsNullOrWhiteSpace(filter)
            ? "Showing all products"
            : $"Searching for \"{filter}\"";
    }
}
```

*What just happened:* each time the bind updates `filter`, Blazor calls `OnFilterChanged`, which reads the freshly-updated value and sets `status`. The key word is *after* - by the time your method runs, `filter` already holds the new value.

> ⚠️ This one bites everyone: **`@bind="x"` is shorthand** for `value="@x"` plus an `@onchange` handler that assigns the typed value back to `x`. Because `@bind` already owns that handler, you **cannot** also add your own `@onchange` to the same element - Blazor will error. Use `@bind:after` (shown above), or drop `@bind` entirely and write `value="@x"` with your own `@onchange` handler doing both the assignment and your logic. Never both on the same element.

## When does the UI actually update?

Binding handles the sync, but *re-rendering* is what makes the change visible. When a bound field changes **because of the UI** (a keystroke, a checkbox click), Blazor re-renders that component automatically - that's why the filtered list updates without you lifting a finger.

> 💡 The exception is state you change from **your own code** outside a UI event - a background timer ticking, a value updated inside an `async` callback. There, Blazor may not know it needs to re-render, and you nudge it with `StateHasChanged()`. Covered in [Events & the Component Lifecycle](04-events-and-lifecycle.md). For now: UI-driven changes render themselves; code-driven changes sometimes need a tap on the shoulder.

## Recap

- **Binding ties markup to state.** One-way (`@field`) flows state → UI; two-way (`@bind`) keeps a form input and a C# field in sync in both directions.
- **One-way** is anything you write as `@expression` in markup - a read-out of your fields. Wrap non-trivial expressions in `@( ... )`.
- **`@bind`** works on text inputs, `<select>`, `<textarea>`, and checkboxes (bound to a `bool`), picking the right attribute and conversion for each. By default it syncs on `onchange` - when the element loses focus.
- **Tune the sync** with `@bind:event="oninput"` (every keystroke), `@bind:format` (e.g. date formatting), and `@bind:after` (run a method once the field has updated).
- **`@bind` is sugar** for `value` + an `@onchange` handler, so you can't add a second manual `@onchange` to the same element - use `@bind:after` or hand-roll value + handler instead.
- **UI-driven changes re-render automatically**; changes from your own code may need `StateHasChanged()` (Phase 4).

## Quick check

```quiz
[
  {
    "q": "By default, when does @bind on a text <input> sync the field?",
    "choices": ["On every keystroke", "When the input loses focus (the onchange event)", "Only when the form is submitted", "Once when the component first renders"],
    "answer": 1,
    "explain": "Default @bind syncs on the onchange event, which fires when the input loses focus. Use @bind:event=\"oninput\" to sync on every keystroke."
  },
  {
    "q": "Why can't you add your own @onchange handler to an element that already uses @bind?",
    "choices": ["@bind only works on read-only elements", "@bind is shorthand for value + an @onchange handler, so it already owns that event", "@onchange is not a valid Blazor event", "You can, but only if the field is a string"],
    "answer": 1,
    "explain": "@bind=\"x\" expands to value=\"@x\" plus an @onchange handler that writes back. Adding another @onchange conflicts; use @bind:after instead."
  },
  {
    "q": "Which directive runs a method right after a bound field has been updated?",
    "choices": ["@bind:format", "@bind:event", "@bind:after", "@onchange"],
    "answer": 2,
    "explain": "@bind:after runs your method once the bind has assigned the new value, so the field already holds the updated value when it runs."
  }
]
```


---

# Events & the Component Lifecycle

So far your components have rendered and bound data. Now we make them *do* things - react
when a user clicks, and run setup code at exactly the right moment. This is where a
component stops being a static page and starts feeling alive.

Here's the whole chapter in one breath - the rest is detail:

- **Events run your C# methods.** A click, a change, a submit - each can call a method in
  your `@code` block.
- **Lifecycle methods run your code at defined moments.** Blazor calls specific methods when
  a component is created, when its parameters change, and after it paints to the screen. You
  override the one that fits your need.
- **`StateHasChanged()` asks Blazor to re-render.** Most of the time Blazor renders for you.
  When state changes somewhere Blazor isn't watching, this is how you say "the screen is stale,
  redraw it."

Hold those three sentences. Everything below hangs off them.

> 📝 We'll keep building the **products** UI from the previous phases: load a list of products
> when the component appears, and add a button that does something. Same component, more life.

## Events: making clicks run your code

You attach a handler to a DOM event with `@on` + the event name, pointing at a method:

```razor
<button @onclick="Refresh">Refresh</button>

<p>Clicked @count times.</p>

@code {
    private int count = 0;

    private void Refresh()
    {
        count++;
    }
}
```

*What just happened:* `@onclick="Refresh"` tells Blazor "when this button is clicked, call the
`Refresh` method." `Refresh` bumps `count`, and because this ran inside a Blazor event handler,
Blazor re-renders automatically afterward - the `<p>` updates with no extra work from you. Same
pattern for `@onchange` (value committed), `@oninput` (every keystroke), and `@onsubmit` (form
submitted).

Sometimes you need details about the event - which key, which mouse button, the new value.
Blazor hands you a strongly-typed event-args object when you ask for it:

```razor
<input @oninput="OnTyping" placeholder="Type something" />

<p>You typed: @text</p>

@code {
    private string text = "";

    private void OnTyping(ChangeEventArgs e)
    {
        text = e.Value?.ToString() ?? "";
    }
}
```

*What just happened:* declaring the parameter as `ChangeEventArgs e` makes Blazor pass the event
data in. `e.Value` is the input's current value (typed as `object?`, so we convert and guard
against null). Different events carry different args - `MouseEventArgs` for clicks,
`KeyboardEventArgs` for key presses - take the parameter only when you need it.

### Async handlers

Real work - calling an API, saving to a database - is asynchronous. Handlers can be `async Task`,
and you wire them up exactly the same way:

```razor
<button @onclick="LoadProducts" disabled="@isLoading">
    @(isLoading ? "Loading..." : "Load products")
</button>

<ul>
    @foreach (var p in products)
    {
        <li>@p.Name - $@p.Price</li>
    }
</ul>

@code {
    private List<Product> products = new();
    private bool isLoading = false;

    private async Task LoadProducts()
    {
        isLoading = true;
        products = await Http.GetFromJsonAsync<List<Product>>("api/products") ?? new();
        isLoading = false;
    }
}
```

*What just happened:* `LoadProducts` is `async Task`, so it can `await` the HTTP call without
freezing the UI. Notice the timing: when you set `isLoading = true` and hit the `await`, Blazor
re-renders *while it waits* (showing "Loading..." and disabling the button). When the `await`
completes, Blazor re-renders *again* (products shown, button re-enabled). You set the flag; Blazor
handles both repaints around the `await`.

> ⚠️ Make async handlers return `Task`, not `void`. An `async void` method can't be awaited, so
> Blazor can't track when it finishes or surface its exceptions - errors just vanish. `async Task`
> is the rule for event handlers.

## The lifecycle: running code at the right moment

A component isn't a one-shot render. It gets *created*, has its *parameters set*, *renders*, and
later *re-renders*. Blazor exposes hooks at each stage. You override the one whose timing matches
what you need.

The three you'll actually reach for:

| Method | When it runs | Use it for |
|--------|-------------|------------|
| `OnInitialized` / `OnInitializedAsync` | Once, when the component is first created | Loading initial data |
| `OnParametersSet` / `OnParametersSetAsync` | Initially **and** every time a parent updates a parameter | Reacting to changed inputs |
| `OnAfterRender` / `OnAfterRenderAsync` | After the component renders to the DOM | JS interop, anything needing the rendered DOM |

### `OnInitializedAsync` - load your initial data

The most common one. This is where the products list should load - automatically, when the
component appears, instead of waiting for a button:

```razor
@if (products is null)
{
    <p>Loading products...</p>
}
else
{
    <ul>
        @foreach (var p in products)
        {
            <li>@p.Name - $@p.Price</li>
        }
    </ul>
}

@code {
    private List<Product>? products;

    protected override async Task OnInitializedAsync()
    {
        products = await Http.GetFromJsonAsync<List<Product>>("api/products");
    }
}
```

*What just happened:* Blazor calls `OnInitializedAsync` once, right after creating the component.
The timing again matters: the component **renders once before the `await` finishes** - at that
point `products` is still `null`, so the reader sees "Loading products...". When the data arrives,
Blazor re-renders and the list appears. That `null` check isn't ceremony; it's the loading state
your reader sees for the first beat. (`List<Product>?` here precisely so `null` can mean "not
loaded yet.")

### `OnParametersSet` - react to the parent

If a parent component passes in a parameter (covered fully in
[Phase 6](06-communication-and-state.md)), `OnParametersSet` runs initially *and* every time the
parent changes that value. It's where you respond to new inputs:

```razor
@code {
    [Parameter]
    public string CategoryId { get; set; } = "";

    private List<Product>? products;

    protected override async Task OnParametersSetAsync()
    {
        products = await Http.GetFromJsonAsync<List<Product>>(
            $"api/products?category={CategoryId}");
    }
}
```

*What just happened:* whenever the parent renders with a different `CategoryId`, Blazor sets the
new value and calls `OnParametersSetAsync`, so the products reload for the new category.
`OnInitializedAsync` would *not* re-run on a parameter change - it only fires once - exactly why
this hook exists.

### `OnAfterRender` - when you need the real DOM

This one runs *after* the markup is on the page. It's the home for JavaScript interop and anything
that has to touch the actual rendered DOM (focusing an element, initializing a JS chart library):

```razor
@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            // Runs only after the very first paint - initialize JS here.
        }
    }
}
```

*What just happened:* `firstRender` is `true` only on the component's first paint and `false` on
every re-render after. Guard one-time setup (like wiring up a JS library) with `if (firstRender)`
so it doesn't re-run on every render. Don't load data here - by the time `OnAfterRender` runs, the
component has already drawn, so you'd cause an extra render. Data loading belongs in
`OnInitializedAsync`.

## `StateHasChanged()` - forcing a re-render

Here's the part that trips people up, so let's be precise.

Blazor automatically re-renders a component after **its own** events: a UI event handler like
`@onclick`, a lifecycle method, or the resumption of an `await` inside one of those. In all those
cases you change state and Blazor redraws - no `StateHasChanged()` needed.

⚠️ The trouble starts when state changes from **outside** that flow - somewhere Blazor isn't
watching. Common culprits:

- a `System.Threading.Timer` callback firing on a background thread,
- a long-running background task completing,
- an event raised by *another* object your component subscribed to.

Blazor has no idea those happened, so it doesn't re-render. You have to tell it:

```razor
<p>Auto-refreshed @refreshCount times.</p>

@code {
    private int refreshCount = 0;
    private System.Threading.Timer? timer;

    protected override void OnInitialized()
    {
        timer = new System.Threading.Timer(_ =>
        {
            refreshCount++;
            InvokeAsync(StateHasChanged);   // tell Blazor to re-render
        }, null, 0, 1000);
    }
}
```

*What just happened:* the timer callback fires every second on a background thread and bumps
`refreshCount` - but nothing on screen would change, because no Blazor event ran. Calling
`StateHasChanged()` is the explicit "redraw me" signal. We wrap it in `InvokeAsync(...)` because
the callback is on a background thread, and Blazor's render must happen on its own thread - 
`InvokeAsync` marshals it back. Inside a normal `@onclick` handler you'd never need this.

> 💡 **The render-loop intuition.** Picture a loop: Blazor **renders** → the user (or a lifecycle
> hook) triggers an **event/handler** → your code changes **state** → Blazor **re-renders**. As
> long as the change happens *inside* that loop, the redraw is free. `StateHasChanged()` kicks the
> loop from the *outside* when something changed that it never saw. If you find yourself reaching
> for it inside an ordinary click handler, pause - you almost certainly don't need it there.

## Recap

- **Events** wire DOM actions to C# with `@onclick`, `@onchange`, `@oninput`, `@onsubmit`. Add a
  typed event-args parameter (`ChangeEventArgs`, `MouseEventArgs`) only when you need the details.
- **Async handlers** return `Task`, never `void`. Blazor re-renders both before the `await` (so set
  a loading flag) and after it resumes.
- **`OnInitializedAsync`** loads initial data once; the component renders before the data arrives,
  so show a loading state (a `null` check).
- **`OnParametersSetAsync`** re-runs whenever a parent changes a parameter - use it to react to new
  inputs; **`OnAfterRender(firstRender)`** is for JS interop and DOM-dependent setup.
- **`StateHasChanged()`** forces a re-render only when state changes *outside* Blazor's own
  handlers/lifecycle (timers, background tasks, external events) - wrap it in `InvokeAsync` from a
  background thread.

## Quick check

```quiz
[
  {
    "q": "Where should you load a component's initial data list so it appears automatically when the component is shown?",
    "choices": ["In OnAfterRender", "In OnInitializedAsync", "In an @onclick handler", "In the @code field initializer with await"],
    "answer": 1,
    "explain": "OnInitializedAsync runs once when the component is created - the standard place for initial data loading. The component renders once before the await completes, so show a loading state."
  },
  {
    "q": "You have a System.Threading.Timer that updates a counter every second, but the screen never changes. Why?",
    "choices": ["Timers don't work in Blazor", "The change happened outside Blazor's event flow, so it didn't re-render - call StateHasChanged via InvokeAsync", "You forgot @bind", "OnParametersSet wasn't overridden"],
    "answer": 1,
    "explain": "Blazor only auto-renders after its own handlers and lifecycle methods. A background timer callback is outside that flow, so you must call StateHasChanged() (wrapped in InvokeAsync from the background thread) to request a redraw."
  },
  {
    "q": "Which lifecycle method re-runs every time a parent component changes a parameter passed to this component?",
    "choices": ["OnInitializedAsync", "OnAfterRender", "OnParametersSet / OnParametersSetAsync", "StateHasChanged"],
    "answer": 2,
    "explain": "OnParametersSet runs initially and again on every parameter update from the parent. OnInitialized only fires once, so it won't react to later parameter changes."
  }
]
```


---

# Forms & Validation

Sooner or later your products UI needs a real form - a screen where someone types a product name, a price, and hits **Save**. The moment you have a form, you have a second job: stopping bad data before it reaches your database. Empty names. Negative prices. A description three paragraphs longer than your column allows.

You could wire all that by hand - track every input, check every value on submit, render every error message yourself. People did, for years. Blazor gives you a system instead, and once you see its shape, you won't want to go back.

## The mental model: a form is a model with a guard

Here's the one idea to hold. In Blazor, **a form is built around a model object** - a plain C# class holding the data being edited. The `<EditForm>` component wraps that model and quietly tracks its state: which fields changed, which are valid, whether the whole thing is ready to submit. That tracking object is the **`EditContext`**, and Blazor creates it the instant you hand `EditForm` a model.

Three moving parts, and they all point at the same model:

1. **The model** - a class with your fields (`Name`, `Price`) plus *annotations* that describe the rules (`[Required]`, `[Range]`).
2. **The inputs** - components like `InputText` that bind to model properties, so typing updates the model and the model updates the screen.
3. **The validator** - a component that reads the model's annotations and decides, field by field, whether the data is valid.

> 💡 The shape to keep in your head: *inputs write to the model, the validator checks the model, the form submits the model.* Everything in this phase is one of those three roles.

Let's build it up piece by piece, using a product form.

## EditForm and the input components

Start with the container. `EditForm` needs a `Model` - the object it's editing - and a handler for when the form is submitted successfully.

```razor
<EditForm Model="@product" OnValidSubmit="Save">
    <label>
        Name
        <InputText @bind-Value="product.Name" />
    </label>

    <label>
        Price
        <InputNumber @bind-Value="product.Price" />
    </label>

    <button type="submit">Save</button>
</EditForm>

@code {
    private ProductForm product = new();

    private void Save()
    {
        // product.Name and product.Price are filled in from the inputs.
    }
}

public class ProductForm
{
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
}
```

*What just happened:* `EditForm` rendered a real HTML `<form>` and built an `EditContext` around `product`. Each input is a **built-in Blazor component** - not a raw `<input>` - and `@bind-Value="product.Name"` is two-way binding (the same `@bind` idea from [Phase 3](03-data-binding.md), here named `Value` because that's the input's parameter). Type in the box, `product.Name` updates; change it in code, the box updates. Click Save, and `OnValidSubmit` runs your `Save` method with the model already populated.

Blazor ships an input component for each common field type. You bind every one of them with `@bind-Value`:

| Component | For |
|-----------|-----|
| `InputText` | single-line text |
| `InputTextArea` | multi-line text |
| `InputNumber` | numeric types (`int`, `decimal`, …) |
| `InputSelect` | dropdowns (`<option>` children) |
| `InputCheckbox` | booleans |
| `InputDate` | dates (`DateTime`, `DateOnly`) |
| `InputRadioGroup` | a set of radio buttons |

> 📝 Why use these instead of plain `<input>` tags? Because the built-in components plug into the `EditContext`. They report changes back to it, and - crucially for the next section - they know how to show themselves as invalid. A raw `<input>` is just HTML; it's outside the form's awareness.

## Validation: annotations plus a validator

Right now nothing stops a user from saving an empty name or a price of negative ten. Let's add rules.

Rules live as **data annotation attributes** on the model. They're declarative - you describe the constraint, not the checking code:

```csharp
using System.ComponentModel.DataAnnotations;

public class ProductForm
{
    [Required(ErrorMessage = "A product needs a name.")]
    [StringLength(120)]
    public string Name { get; set; } = "";

    [Range(0, 100000, ErrorMessage = "Price must be between 0 and 100,000.")]
    public decimal Price { get; set; }
}
```

*What just happened:* nothing yet - that's the whole catch. `[Required]`, `[StringLength]`, and `[Range]` are passive metadata, sitting on the class doing nothing until something reads them. Common annotations: `[Required]`, `[StringLength(max)]`, `[Range(min, max)]`, `[EmailAddress]`.

> ⚠️ The annotations alone do **nothing**. They're inert until you place a `<DataAnnotationsValidator />` inside the `EditForm`. This is the single most common Blazor forms mistake - a form that "ignores" its rules almost always means the validator component is missing. Attributes describe the rules; the component enforces them.

So you add the validator, plus a way to show the errors. `<ValidationSummary />` lists every error at once; `<ValidationMessage For="..." />` shows the error for one specific field, right next to it. Here's the full, working form:

```razor
@using System.ComponentModel.DataAnnotations

<EditForm Model="@product" OnValidSubmit="Save">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <label>
        Name
        <InputText @bind-Value="product.Name" />
        <ValidationMessage For="@(() => product.Name)" />
    </label>

    <label>
        Price
        <InputNumber @bind-Value="product.Price" />
        <ValidationMessage For="@(() => product.Price)" />
    </label>

    <button type="submit">Save</button>
</EditForm>

@code {
    private ProductForm product = new();

    private void Save()
    {
        // We only get here if every annotation passed.
    }
}

public class ProductForm
{
    [Required(ErrorMessage = "A product needs a name.")]
    [StringLength(120)]
    public string Name { get; set; } = "";

    [Range(0, 100000)]
    public decimal Price { get; set; }
}
```

*What just happened:* `<DataAnnotationsValidator />` hooked into the `EditContext` and started reading the model's annotations. When the user edits a field or tries to submit, it validates against the rules and marks fields valid or invalid. `<ValidationSummary />` renders the full error list near the top; each `<ValidationMessage For="@(() => product.Name)" />` renders just that field's error inline. The `For` argument is a lambda that *points at* the property - how the message component knows which field it represents.

> 💡 Look closely at `ProductForm`: a plain class with `[Required]` and `[Range]` - the **exact same** `System.ComponentModel.DataAnnotations` attributes you'd put on an ASP.NET Core DTO or an EF Core entity. The knowledge transfers directly - if you've validated a request body in a Web API, you already know how to validate a Blazor form.

## Submit events: OnValidSubmit vs OnSubmit

`EditForm` gives you three submit events. Pick based on who does the validating.

- **`OnValidSubmit`** - fires *only* when validation passes. The one you'll reach for most: by the time your handler runs, the model is guaranteed valid, so you save straight away. (Pair it with `OnInvalidSubmit` to react to *failed* attempts - say, scroll to the first error.)
- **`OnSubmit`** - fires on *every* submit, valid or not. Blazor won't gate the handler; *you* call validation yourself and decide what to do.

You almost always want the first. Here's the contrast:

```razor
@* Clean path: handler only runs on valid data *@
<EditForm Model="@product" OnValidSubmit="Save" OnInvalidSubmit="ShowProblems">
    <DataAnnotationsValidator />
    <!-- inputs… -->
    <button type="submit">Save</button>
</EditForm>

@* Manual path: you validate, you decide *@
<EditForm Model="@product" OnSubmit="HandleEverything">
    <DataAnnotationsValidator />
    <!-- inputs… -->
    <button type="submit">Save</button>
</EditForm>

@code {
    private ProductForm product = new();

    private void Save() { /* product is valid - persist it */ }
    private void ShowProblems() { /* at least one field failed */ }

    private void HandleEverything(EditContext ctx)
    {
        if (ctx.Validate())
        {
            // valid - go ahead
        }
        // ctx tells you the state; you choose what happens next
    }
}
```

*What just happened:* the first form splits the two outcomes cleanly - `Save` for success, `ShowProblems` for failure - and you never write an `if` to check validity. The second routes everything through one handler receiving the `EditContext`; you call `ctx.Validate()` yourself and branch on the result. Use `OnSubmit` only when you need that manual control; for ordinary save-this-product forms, `OnValidSubmit` is less code and harder to get wrong.

> 💡 Data annotations are perfect for field-level rules. For richer logic - "this field is required *only if* that other one is set," cross-field comparisons - reach for **FluentValidation**, which has community Blazor integrations slotting into the same `EditContext`. Start with annotations; graduate when the rules outgrow attributes.

## Recap

- A Blazor form is built around a **model object**; `<EditForm Model="@model">` wraps it and tracks its state in an `EditContext`.
- Use the **built-in input components** (`InputText`, `InputNumber`, `InputSelect`, …) and bind them with `@bind-Value` so they participate in the form's validation.
- Validation rules are **data annotations** (`[Required]`, `[StringLength]`, `[Range]`, `[EmailAddress]`) on the model - the same attributes you'd use on an ASP.NET Core DTO.
- Annotations do nothing on their own: you **must** add `<DataAnnotationsValidator />` inside the form, then show errors with `<ValidationSummary />` and `<ValidationMessage For="@(() => model.Field)" />`.
- Prefer **`OnValidSubmit`** (runs only when valid); use `OnSubmit` when you want to call `EditContext.Validate()` and handle outcomes manually. For complex rules, consider FluentValidation.

## Quick check

```quiz
[
  {
    "q": "You added [Required] and [Range] to your model, but the form saves invalid data anyway. What's the most likely cause?",
    "choices": ["The model class is in the wrong namespace", "There's no <DataAnnotationsValidator /> inside the EditForm", "You used InputText instead of a plain <input>", "OnValidSubmit doesn't support validation"],
    "answer": 1,
    "explain": "Data annotations are inert metadata. Nothing checks them until a <DataAnnotationsValidator /> component is placed inside the EditForm."
  },
  {
    "q": "How do you bind a built-in input component to a model property?",
    "choices": ["value=\"product.Name\"", "@bind=\"product.Name\"", "@bind-Value=\"product.Name\"", "Model=\"product.Name\""],
    "answer": 2,
    "explain": "Built-in input components expose a Value parameter, so two-way binding uses @bind-Value=\"product.Name\"."
  },
  {
    "q": "You want a handler that runs only when the form's data passes validation. Which event do you use?",
    "choices": ["OnSubmit", "OnInvalidSubmit", "OnValidSubmit", "OnChange"],
    "answer": 2,
    "explain": "OnValidSubmit fires only after validation succeeds, so the model is guaranteed valid inside the handler - no manual check needed."
  }
]
```


---

# Component Communication & State

By now you can build a single component and even a tree of them - a products page holding three `<ProductCard />` tags from Phase 2, each its own markup-plus-`@code` unit. But a tree where the pieces can't talk to each other isn't an app; it's a static page. The page needs to hand each card its own product. A card needs to tell the page "the user clicked me." And a cart, off to the side, needs to know about purchases happening in components it has never heard of.

Here's the mental model to hold before any code. **Components talk over four channels, and you pick the channel by who needs to reach whom:**

1. **Parameters down** - a parent hands data to a direct child (`[Parameter]`).
2. **Events up** - a child notifies its parent that something happened (`EventCallback<T>`).
3. **Cascading values** - a value flows down to *any* descendant, however deep, without being threaded through every level in between (`CascadingValue` / `[CascadingParameter]`).
4. **A shared service** - app-wide state that *unrelated* components read and write, living outside the tree entirely (a class registered in DI).

The first two are the workhorses you'll use constantly; the last two solve the "this is getting awkward" problems the first two create at scale.

> 💡 Rule of thumb: parameters and events are for components that already know about each other (parent and direct child). Cascading values and the shared service are for when threading data through every intermediate component would be miserable, or the components have no parent-child relationship at all.

## Channel 1: parameters down (`[Parameter]`)

A **parameter** is a public property on the child marked with `[Parameter]`. The parent sets it by writing an HTML-style attribute on the child's tag. This is the channel that finally lets each `ProductCard` show its *own* product instead of a hardcoded one.

Here's `ProductCard` accepting a `Product` from its parent:

```razor
@* ProductCard.razor *@
<div class="card">
    <h3>@Product.Name</h3>
    <p>@Product.Price.ToString("C")</p>
</div>

@code {
    [Parameter]
    public Product Product { get; set; } = default!;
}
```

And the products page passing one in:

```razor
@page "/products"

<div class="grid">
    @foreach (var p in products)
    {
        <ProductCard Product="p" />
    }
</div>

@code {
    private List<Product> products = new()
    {
        new() { Name = "Mechanical Keyboard", Price = 89.99m },
        new() { Name = "USB-C Hub", Price = 34.50m },
        new() { Name = "Laptop Stand", Price = 42.00m },
    };
}
```

*What just happened:* the child declared `[Parameter] public Product Product { get; set; }` - a normal C# property plus the attribute telling Blazor "the parent is allowed to set this." The parent's `<ProductCard Product="p" />` looks like an HTML attribute, but the value (`p`) is real C#: the current loop variable. Each `@foreach` iteration renders one card with a different product - three products, three cards. `= default!;` is a C# nicety: it promises the compiler the parameter will be set, silencing the nullable warning.

> 📝 Parameters flow **one way: parent to child.** A child should *read* its parameters, not reassign them - if it writes to its own `[Parameter]` property, Blazor overwrites that value the next time the parent re-renders, and your change vanishes. When a child needs to send something *back*, that's channel 2.

### Passing markup, not just data: child content

Sometimes a parent doesn't want to pass a value - it wants to pass *markup* to be rendered inside the child. A reusable `<Card>` wrapper that draws a border and padding but lets the caller decide what goes inside is the classic case. Blazor handles this with a special parameter type, `RenderFragment`, conventionally named `ChildContent`:

```razor
@* Card.razor *@
<div class="card">
    @ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}
```

Now any component can nest content between the `<Card>` tags:

```razor
<Card>
    <h3>Mechanical Keyboard</h3>
    <p>Clicky. Loud. Worth it.</p>
</Card>
```

*What just happened:* when you put markup *between* a component's opening and closing tags, Blazor captures it as a `RenderFragment` and assigns it to the parameter named `ChildContent`. `Card` renders `@ChildContent` wherever it wants the caller's markup to land - here, inside the bordered `<div>`. This is how you build layout and wrapper components: the wrapper owns the chrome, the caller owns the contents.

## Channel 2: events up (`EventCallback<T>`)

A child can't reach up and call a method on its parent - it doesn't even know what its parent is. Instead, the parent hands the child a **callback**, and the child invokes it when something happens. In Blazor that callback is an `EventCallback<T>`: a parameter the child exposes, wired by the parent to one of its own methods.

Let's make `ProductCard` tell its parent when it's clicked:

```razor
@* ProductCard.razor *@
<div class="card" @onclick="HandleClick">
    <h3>@Product.Name</h3>
    <p>@Product.Price.ToString("C")</p>
</div>

@code {
    [Parameter]
    public Product Product { get; set; } = default!;

    [Parameter]
    public EventCallback<Product> OnSelected { get; set; }

    private async Task HandleClick()
    {
        await OnSelected.InvokeAsync(Product);
    }
}
```

The parent provides the handler:

```razor
@page "/products"

<p>Selected: @(selected?.Name ?? "nothing yet")</p>

<div class="grid">
    @foreach (var p in products)
    {
        <ProductCard Product="p" OnSelected="HandleSelected" />
    }
</div>

@code {
    private Product? selected;

    private void HandleSelected(Product product)
    {
        selected = product;
    }

    private List<Product> products = new() { /* ...as before... */ };
}
```

*What just happened:* the child exposes `OnSelected` as an `EventCallback<Product>` parameter. When its `<div>` is clicked, `HandleClick` runs and calls `OnSelected.InvokeAsync(Product)`, passing the clicked product upward. The parent set `OnSelected="HandleSelected"`, so its `HandleSelected` method runs with that product, updating `selected`. Data went *up* the tree - child to parent - which parameters alone can't do. The parent's `<p>` re-renders to show the new selection.

> 📝 Why `EventCallback<T>` and not a plain `Action<T>` or C# `event`? Because `EventCallback` is **Blazor-aware**: after the handler runs, Blazor automatically re-renders the parent. With a raw `Action`, the parent's method would run, but Blazor wouldn't know its state changed, so the UI wouldn't update until something else triggered a render - you'd have to call `StateHasChanged` by hand, exactly the trap channel 4 warns about.

### Two-way component binding (`@bind-Value`)

There's a shorthand built on these two channels. If a component exposes a `Value` parameter *and* a matching `ValueChanged` event (`EventCallback<T>`), a parent can bind to it with `@bind-Value`, and changes flow both directions automatically:

```razor
<MyTextBox @bind-Value="searchTerm" />
```

*What just happened:* `@bind-Value="searchTerm"` expands to setting `Value="searchTerm"` (parent → child, channel 1) *and* wiring `ValueChanged` to update `searchTerm` (child → parent, channel 2) in one line. This is exactly the pattern the built-in `InputText` uses, and why `@bind-Value` worked in the Phase 5 form. Build your own input-like components following the `Value` + `ValueChanged` naming convention and callers get `@bind-Value` for free.

## Channel 3: cascading values for deep data

Parameters work beautifully parent-to-child. But imagine a value the *whole tree* needs - the current theme, the logged-in user, an app config. With parameters alone, you'd declare it on every component between the top and the place it's used, passing it down level by level, even through components that don't care about it. That tedious threading is called "prop drilling," and **cascading values** exist to skip it.

A parent wraps part of the tree in `<CascadingValue>`, and *any* descendant - at any depth - can pull it out with `[CascadingParameter]`:

```razor
@* App layout - somewhere near the top of the tree *@
<CascadingValue Value="theme">
    <ProductsPage />
</CascadingValue>

@code {
    private Theme theme = new() { Accent = "teal", Dark = true };
}
```

A deeply-nested `ProductCard` - without any intermediate component knowing about `theme` - reaches in:

```razor
@* ProductCard.razor, deep inside the tree *@
<div class="card @(Theme.Dark ? "dark" : "light")">
    <h3 style="color:@Theme.Accent">@Product.Name</h3>
    <p>@Product.Price.ToString("C")</p>
</div>

@code {
    [Parameter]
    public Product Product { get; set; } = default!;

    [CascadingParameter]
    public Theme Theme { get; set; } = default!;
}
```

*What just happened:* `<CascadingValue Value="theme">` makes `theme` available to everything rendered inside it, however many components deep. `ProductCard` grabbed it with `[CascadingParameter] public Theme Theme { get; set; }` - no `Theme="..."` attribute on the card's tag, and no intermediate component had to forward it. Blazor matches cascading values to cascading parameters **by type** by default. This is the right channel for cross-cutting, tree-wide data; for ordinary parent-to-child data, use a plain `[Parameter]`.

> ⚠️ Cascading values are matched by type. If you ever need *two* cascading values of the same type in scope, give them a `Name` (`<CascadingValue Value="x" Name="Primary">`) and match it on the parameter (`[CascadingParameter(Name = "Primary")]`), or Blazor can't tell them apart.

## Channel 4: app-wide shared state via a service

Cascading values still flow *down* a tree. But some state belongs to no single tree - a shopping cart, say. The products page adds to it; a cart badge in the navbar reads from it; a checkout page on a different route reads it too. These components are siblings or strangers, not ancestors and descendants. The answer: move that state **out of the component tree entirely** into a plain C# class, register it as a **DI service**, and inject it wherever needed.

Here's a minimal cart state class:

```csharp
public class CartState
{
    private readonly List<Product> items = new();

    public IReadOnlyList<Product> Items => items;

    public event Action? OnChange;

    public void Add(Product product)
    {
        items.Add(product);
        OnChange?.Invoke();
    }
}
```

You register it once at startup (in `Program.cs`):

```csharp
builder.Services.AddScoped<CartState>();
```

*What just happened:* `CartState` is an ordinary class holding the list of items, plus an `OnChange` event it raises on change. Registering it with `AddScoped` means Blazor hands the *same* instance to every component that asks for it, within a user's session. 📝 The lifetime choice differs by hosting model from Phase 1: **`Scoped` in Blazor Server** (one instance per user connection), typically **`Singleton` in Blazor WebAssembly** (the whole app is one user in one browser tab anyway). Pick the one matching your host.

Any component injects it with `@inject` (the directive you met in Phase 2) and uses it:

```razor
@* In ProductCard - add to cart on selection *@
@inject CartState Cart

<button @onclick="() => Cart.Add(Product)">Add to cart</button>
```

```razor
@* CartBadge.razor - lives in the navbar, far from ProductCard *@
@inject CartState Cart
@implements IDisposable

<span class="badge">@Cart.Items.Count</span>

@code {
    protected override void OnInitialized()
    {
        Cart.OnChange += StateHasChanged;
    }

    public void Dispose()
    {
        Cart.OnChange -= StateHasChanged;
    }
}
```

*What just happened:* both components inject the same `CartState`. The button in `ProductCard` calls `Cart.Add(Product)`, which mutates the shared list and fires `OnChange`. `CartBadge` - which `ProductCard` has never heard of - subscribed to `OnChange` in `OnInitialized`, so when the event fires it calls `StateHasChanged` and re-renders its count. State changed in one component; a completely unrelated component updated in response - the whole point of the service channel.

> ⚠️ The gotcha that bites everyone once. A component does **not** automatically re-render when state *outside it* changes - Blazor only re-renders on its own parameters, its own events, or an explicit `StateHasChanged`. A component reading shared state must (1) **subscribe** to the service's change event, (2) call **`StateHasChanged`** in the handler, and (3) **unsubscribe in `Dispose`** (via `@implements IDisposable`). Skip the subscribe and the badge silently shows a stale count; skip the unsubscribe and you leak the component. All three steps, every time.

## Recap

- Components talk over **four channels**: parameters **down**, events **up**, cascading values for the **deep tree**, and a shared **service** for app-wide state. Pick by who needs to reach whom.
- **`[Parameter]`** sends data parent → child; the parent sets it as an attribute (`<ProductCard Product="p" />`). A `RenderFragment? ChildContent` parameter lets a parent nest **markup** inside the child.
- **`EventCallback<T>`** sends notifications child → parent; the child calls `OnSelected.InvokeAsync(...)`, the parent handles it. It's preferred over a plain `Action` because it **auto-re-renders** the parent. A `Value` + `ValueChanged` pair gives callers `@bind-Value`.
- **`CascadingValue` / `[CascadingParameter]`** push a value to any descendant without threading it through every level - for cross-cutting data like a theme or current user. Matched by type (use `Name` to disambiguate).
- **A DI-registered state service** holds app-wide state outside the tree (`Scoped` on Server, usually `Singleton` on WebAssembly). It raises an event on change.
- A component **won't** re-render on external state changes unless it **subscribes**, calls **`StateHasChanged`**, and **unsubscribes in `Dispose`** - all three, or you get a stale UI or a leak.

## Quick check

Make sure the four channels are straight before moving on:

```quiz
[
  {
    "q": "A child ProductCard needs to tell its parent page which product the user clicked. Which channel fits?",
    "choices": ["A [Parameter] on the child", "An EventCallback<Product> the child invokes", "A CascadingValue wrapping the page", "Reassigning the child's own parameter"],
    "answer": 1,
    "explain": "Child-to-parent notification is exactly what EventCallback<T> is for. The child calls OnSelected.InvokeAsync(product); the parent handles it - and the parent auto-re-renders."
  },
  {
    "q": "Why is EventCallback<T> preferred over a plain Action<T> for child-to-parent communication in Blazor?",
    "choices": ["Action cannot carry a value", "EventCallback automatically triggers a re-render of the parent after the handler runs", "Action only works in Blazor WebAssembly", "EventCallback is faster at runtime"],
    "answer": 1,
    "explain": "EventCallback is Blazor-aware: after the handler runs it re-renders the parent. With a raw Action the parent's method runs but the UI won't update until something else triggers a render."
  },
  {
    "q": "A CartBadge in the navbar injects the shared CartState service but its count never updates when items are added elsewhere. What's missing?",
    "choices": ["It needs a [Parameter] for the count", "It must subscribe to the service's change event and call StateHasChanged (and unsubscribe in Dispose)", "CartState must be registered as Transient", "The badge needs its own @page route"],
    "answer": 1,
    "explain": "A component doesn't re-render on external state changes by itself. It must subscribe to the service's OnChange event, call StateHasChanged in the handler, and unsubscribe in Dispose to avoid a leak."
  }
]
```


---

# Calling APIs & Dependency Injection

Up to now your components have been self-contained: they held their own state and
re-rendered when it changed. Real apps aren't like that. A component needs *collaborators* - 
something that fetches data, talks to a backend, logs an error. This phase is about how a
component **gets** those collaborators and how it **reaches a server** to load real data.

Here's the whole chapter in one mental model - the rest is detail:

- **Components don't build their own dependencies - they ask for them.** A component declares
  "I need a data service" and Blazor hands it one. That's **dependency injection (DI)**, and
  it's the *exact same* DI container as ASP.NET Core - you've met it already if you've done
  [ASP.NET Core](/guides/aspnet-core-from-zero).
- **To reach a backend, you inject an `HttpClient`** (or, better, a service that wraps one)
  and call JSON helpers like `GetFromJsonAsync`. The backend is a normal ASP.NET Core API.

Hold those two sentences. Everything below hangs off them.

> 📝 We'll keep building the **products** UI: this time it loads the product list from a real
> API and lets the reader create a new product that gets POSTed to the backend. The API itself
> is an ASP.NET Core service - see [ASP.NET Core From Zero](/guides/aspnet-core-from-zero) for
> the other side of the wire.

## The mental model: injected collaborators

Imagine a component that needs to fetch products. The naive instinct is to have the component
`new` up whatever it needs:

```csharp
// The instinct we're going to NOT follow.
var http = new HttpClient();
var service = new ProductService(http);
```

The problem: the component is now welded to those concrete types. You can't swap the service
for a fake one in a test, can't configure the `HttpClient` in one place, and every component
that needs products repeats this wiring.

DI flips it. You register your services **once**, centrally, and components *ask* for
what they need. Blazor's container constructs them, wires up their own dependencies, and hands
the finished object over. The component never says `new` - it says "give me one."

This is the same `IServiceProvider` container ASP.NET Core uses, with the same lifetimes
(`Scoped`, `Singleton`, `Transient`). If DI in ASP.NET Core already clicked for you, you
already understand Blazor's.

## Registering services and injecting them

Registration happens in `Program.cs`, where the app is wired up:

```csharp
// Program.cs
builder.Services.AddScoped<IProductService, ProductService>();
```

*What just happened:* you told the container "whenever something asks for an `IProductService`,
build a `ProductService` and reuse it for the lifetime of this scope." Registering against the
**interface** is the move that makes the component swappable later - it depends on the
contract, not the concrete class.

Now a component asks for it with the **`@inject`** directive at the top of the `.razor` file:

```razor
@inject IProductService Products

<ul>
    @if (products is not null)
    {
        @foreach (var p in products)
        {
            <li>@p.Name - $@p.Price</li>
        }
    }
</ul>

@code {
    private List<Product>? products;

    protected override async Task OnInitializedAsync()
    {
        products = await Products.GetAllAsync();
    }
}
```

*What just happened:* `@inject IProductService Products` declares a property named `Products`
that Blazor fills in before the component renders. By the time `OnInitializedAsync` runs (the
load-your-data hook from [Phase 4](04-events-and-lifecycle.md)), `Products` is ready to use. The
component calls `Products.GetAllAsync()` - it has no idea there's an `HttpClient` behind it,
which is the whole point.

If you prefer attributes over the directive (handy in a code-behind file or a base class), the
equivalent inside `@code` is:

```csharp
[Inject]
public IProductService Products { get; set; } = default!;
```

*What just happened:* `[Inject]` does the same job as `@inject` - Blazor sets this property
after constructing the component. `= default!` quiets the nullable-reference compiler warning,
since DI assigns it before any of your code runs.

> 💡 `@inject` is the directive form (top of the markup); `[Inject]` is the attribute form
> (inside `@code`). Same mechanism, no behavioral difference - pick whichever reads better.

## Calling an API with HttpClient

Now the part where data actually crosses the network. Blazor uses the standard .NET
**`HttpClient`**, paired with the JSON helper extension methods in **`System.Net.Http.Json`**.
These helpers serialize and deserialize JSON for you, so you work in typed objects, not raw
strings.

The four you'll use constantly:

| Method | What it does |
|--------|--------------|
| `GetFromJsonAsync<T>(url)` | GET, deserialize the JSON response into a `T` |
| `PostAsJsonAsync(url, obj)` | POST `obj` as JSON in the body |
| `PutAsJsonAsync(url, obj)` | PUT `obj` as JSON (update) |
| `DeleteAsync(url)` | DELETE the resource at the URL |

Here's the products list loading from the API directly (we'll improve on the raw-HttpClient
approach in a moment), with the loading state from [Phase 4](04-events-and-lifecycle.md):

```razor
@inject HttpClient Http

@if (products is null)
{
    <p>Loading products...</p>
}
else
{
    <ul>
        @foreach (var p in products)
        {
            <li>@p.Name - $@p.Price</li>
        }
    </ul>
}

@code {
    private List<Product>? products;

    protected override async Task OnInitializedAsync()
    {
        products = await Http.GetFromJsonAsync<List<Product>>("api/products");
    }
}
```

*What just happened:* `GetFromJsonAsync<List<Product>>("api/products")` fires a GET to
`api/products`, reads the JSON array, and deserializes it into a `List<Product>`. Because the
call is `await`ed inside `OnInitializedAsync`, the component renders **once before** the data
arrives (`products` is still `null`, so the reader sees "Loading products..."), then re-renders
when it lands - the same `null`-as-loading-state pattern from the lifecycle phase.

Creating a product is the write side. A small form POSTs a new product, then refreshes the list:

```razor
@inject HttpClient Http

<input @bind="newName" placeholder="Product name" />
<input @bind="newPrice" type="number" placeholder="Price" />
<button @onclick="Create" disabled="@isSaving">
    @(isSaving ? "Saving..." : "Add product")
</button>

@code {
    private string newName = "";
    private decimal newPrice;
    private bool isSaving;
    private List<Product>? products;

    private async Task Create()
    {
        isSaving = true;
        var newProduct = new Product { Name = newName, Price = newPrice };
        await Http.PostAsJsonAsync("api/products", newProduct);
        products = await Http.GetFromJsonAsync<List<Product>>("api/products");
        newName = "";
        newPrice = 0;
        isSaving = false;
    }
}
```

*What just happened:* `PostAsJsonAsync("api/products", newProduct)` serializes `newProduct`
to JSON and POSTs it. After it returns, we re-fetch the list so the new item shows up, then
clear the inputs. Setting `isSaving` around the `await` gives the reader feedback and disables
the button so a double-click can't double-submit - Blazor re-renders before the `await` (button
shows "Saving...") and again after it resumes (re-enabled, list refreshed).

## ⚠️ WebAssembly vs Server: where the HttpClient call comes from

This is the gotcha that bites people, so let's be precise. **How you register the `HttpClient`
differs between the two hosting models** (Phase 1), because the code runs in different places.

**Blazor WebAssembly** - the C# runs *in the browser*. So the HTTP call leaves the browser,
just like a `fetch()` would. You register an `HttpClient` with a `BaseAddress`:

```csharp
// Program.cs (Blazor WebAssembly)
builder.Services.AddScoped(sp => new HttpClient
{
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
```

*What just happened:* every component that injects `HttpClient` gets one pointed at your app's
base URL, and requests originate from the user's browser. ⚠️ Because it's a real browser
request to (potentially) a different origin, **CORS applies** - if your API is on a different
domain/port, it must send the right CORS headers or the browser blocks the call. That's a
server-side configuration on the API, not something the Blazor component can fix.

**Blazor Server** - the C# runs *on the server*. There's no browser making the request, so
there's **no browser-CORS issue**. The idiomatic approach here is a typed client registered via
`IHttpClientFactory`, which manages connection pooling and lets you configure the client in one
place:

```csharp
// Program.cs (Blazor Server)
builder.Services.AddHttpClient<IProductService, ProductService>(client =>
{
    client.BaseAddress = new Uri("https://localhost:5001/");
});
```

*What just happened:* `AddHttpClient<IProductService, ProductService>` registers
`ProductService` **and** injects a properly-configured `HttpClient` into its constructor - one
call wires up both. Because the request runs server-to-server, browser CORS policy never enters
the picture.

> ⚠️ The trap is copying a WASM `HttpClient` registration into a Server app (or vice versa) and
> being surprised. The rule of thumb: **WASM** = `HttpClient` with `BaseAddress`, mind CORS;
> **Server** = typed client / `IHttpClientFactory`, no browser CORS.

## 💡 The clean pattern: wrap HttpClient in a typed service

You saw components inject `HttpClient` directly above. That works, but it scatters URLs and
HTTP details across your UI. The pattern that scales: **wrap `HttpClient` in a typed service**
and inject *that* into components.

```csharp
public interface IProductService
{
    Task<List<Product>> GetAllAsync();
    Task CreateAsync(Product product);
}

public class ProductService : IProductService
{
    private readonly HttpClient _http;

    public ProductService(HttpClient http) => _http = http;

    public async Task<List<Product>> GetAllAsync() =>
        await _http.GetFromJsonAsync<List<Product>>("api/products") ?? new();

    public async Task CreateAsync(Product product) =>
        await _http.PostAsJsonAsync("api/products", product);
}
```

*What just happened:* the `HttpClient` and the API URLs now live in exactly one place. The
constructor takes an `HttpClient`, which DI injects automatically because you registered the
client alongside the service. Components go back to the clean form from earlier: inject
`IProductService`, call `Products.GetAllAsync()`, and stay unaware of HTTP.

Why this is worth the extra interface:

- **Testable.** A test can supply a fake `IProductService` that returns canned products - no
  network, no server, instant.
- **Swappable.** Move from one API to another, add caching, or add retry logic in *one* class;
  no component changes.
- **Clean boundaries.** Components do UI; the service does data. Each stays small.

This is the same dependency-inversion idea ASP.NET Core leans on everywhere - your components
depend on the `IProductService` *contract*, and DI decides which concrete implementation
satisfies it. If you haven't built the API side, [ASP.NET Core From Zero](/guides/aspnet-core-from-zero) is its companion guide.

## Recap

- **Components ask for collaborators; they don't build them.** Register services in
  `Program.cs` (`builder.Services.AddScoped<IProductService, ProductService>()`), then pull them
  in with `@inject IProductService Products` (or `[Inject]` inside `@code`). It's the same DI
  container as ASP.NET Core.
- **Reach a backend with `HttpClient`** plus the `System.Net.Http.Json` helpers:
  `GetFromJsonAsync<T>`, `PostAsJsonAsync`, `PutAsJsonAsync`, `DeleteAsync` - you work in typed
  objects, not raw JSON strings.
- **Load data in `OnInitializedAsync`** with a `null` loading state; set a saving flag around
  writes so the UI gives feedback and can't double-submit.
- **WASM vs Server registration differs:** WASM registers an `HttpClient` with a `BaseAddress`
  and the call leaves the browser (mind **CORS**); Server uses a typed client /
  `IHttpClientFactory` and runs server-to-server (no browser CORS).
- **Wrap `HttpClient` in a typed `IProductService`** and inject that - it keeps URLs in one
  place and makes the component testable and swappable.

## Quick check

```quiz
[
  {
    "q": "How does a Blazor component get a service it depends on, like IProductService?",
    "choices": ["It calls new ProductService() in OnInitialized", "It declares it with @inject (or [Inject]); Blazor's DI container supplies it", "It reads it from a global static field", "It passes it in as a [Parameter] from the parent"],
    "answer": 1,
    "explain": "Components ask for dependencies via @inject or [Inject], and the DI container - the same one ASP.NET Core uses - constructs and supplies them. The component never news up its own collaborators."
  },
  {
    "q": "In Blazor WebAssembly, the HTTP call to your API runs in the browser and your API is on a different origin. What must be configured for the call to succeed?",
    "choices": ["Nothing - WASM bypasses browser security", "CORS headers on the API, because the request is a real cross-origin browser request", "A typed client via IHttpClientFactory only", "StateHasChanged() after the call"],
    "answer": 1,
    "explain": "WASM runs in the browser, so the request is subject to CORS. If the API is a different origin it must send the right CORS headers. Blazor Server runs server-side and doesn't hit this - that's the key WASM-vs-Server difference."
  },
  {
    "q": "Why wrap HttpClient inside a typed IProductService instead of injecting HttpClient straight into components?",
    "choices": ["It's required - components can't inject HttpClient", "It centralizes URLs and HTTP details, and lets you swap in a fake service for testing", "It makes the HTTP calls run faster", "It avoids needing Program.cs registration"],
    "answer": 1,
    "explain": "A typed service keeps URLs and HTTP details in one place and lets components depend on the IProductService contract - so tests can supply a fake, and you can change the backend without touching the UI."
  }
]
```


---

# Where to Go Next

Stop and look at what you can actually do now. Build a **component** in a `.razor` file, mix markup with C# in an `@code` block, and have it re-render when its state changes. **Bind** inputs with `@bind`, wire up **events** with `@onclick`, hook into the **lifecycle** with `OnInitialized` and `OnParametersSet`, and nudge a re-render with `StateHasChanged`. Build **forms** with `EditForm` and validation, pass data between components with `[Parameter]` and `EventCallback`, share state through a DI service, and `@inject` an `HttpClient` to **load real data from an API**.

That's not a toy. That's interactive web UI - written in C#, with the same language, types, and tooling you already use on the server. You read the machine now.

So this last phase isn't more attributes to memorize. It's the map: where Blazor sits next to the JavaScript frameworks, a recap of the render modes so you choose them on purpose, the libraries you'll reach for next, and one concrete thing to build.

## Blazor vs the JavaScript frameworks

You'll get asked this, probably in an interview: "Why Blazor instead of React?" The real answer isn't "Blazor is better." It's "they're aimed at different teams and different jobs." This is the same lens [What a Framework Even Is](/guides/what-a-framework-even-is) taught - pick the tool that fits the work, not the loudest one.

```mermaid
flowchart TD
  Q{What's the situation?}
  Q -->|".NET team, shared C# types,<br/>line-of-business app"| B[Blazor]
  Q -->|"JS-centric team, huge public<br/>consumer SPA, vast ecosystem"| J[React / Vue / Angular]
```

Here's the straight breakdown:

- **Where Blazor wins.** You build the UI in **C#** - the same language, model classes, and validation attributes shared with your backend. For a .NET team, that's enormous: no context-switching to a separate JS toolchain, no duplicating types in TypeScript, one debugger across the whole stack. For internal tools and **line-of-business apps**, Blazor is a genuinely strong, productive choice.
- **Where a JS framework wins.** React, Vue, and Angular have a **far larger ecosystem** - more components, more hiring pool, more Stack Overflow answers. Blazor WebAssembly ships a .NET runtime to the browser, so its **initial download is larger** than a typical JS bundle. For a heavy **public-facing consumer SPA** where first-load size is critical, or a team that already lives and breathes JavaScript, a JS framework often fits better.

💡 Notice what's *not* on either list: "and the other one is bad." Both build UIs from the same core idea - a tree of components that re-render when their data changes. Learn that idea here, and you've already learned the hard part of any of them.

## Render modes: choose per page (.NET 8)

Back in Phase 1 you met Server vs WebAssembly. Modern .NET (8 and onward) turned that into a **per-page decision** instead of a whole-app one, and it's worth holding the four options clearly because picking deliberately is most of the skill:

- **Static SSR** - the page renders to HTML on the server once, with no interactivity. Fast, cheap, great for content. No event handlers wired up.
- **InteractiveServer** - interactivity runs on the server; UI updates stream to the browser over a live **SignalR** connection. Tiny download, but every click needs a round-trip, and it needs a steady connection.
- **InteractiveWebAssembly** - the C# runs **in the browser** on the .NET runtime. Works offline, no per-click round-trip, but pays that larger initial download.
- **InteractiveAuto** - uses Server for the **first** load (fast startup), then quietly switches to **WebAssembly** for later visits once the runtime is cached. The "best of both" default for many apps.

📝 The tradeoff is always the same triangle: **download size vs latency vs offline**. A marketing page wants static SSR. A live dashboard behind a login is happy on InteractiveServer. An app that must work on a flaky connection wants WebAssembly. You don't have to pick one for the whole app - that's the point.

## Libraries you'll reach for

You don't have to hand-build every button and dialog. The Blazor ecosystem has mature **component libraries** - ready-made, styled UI components (data grids, date pickers, modals, charts):

- **MudBlazor** - popular, Material-design-flavored, free and open source.
- **Radzen** - a big free component set with an optional paid design studio.
- **Fluent UI Blazor** - Microsoft's own, matching the Fluent/Windows look.
- **Telerik UI for Blazor** - a polished commercial suite for teams that want vendor support.

For **state** in larger apps, the DI state-service pattern from Phase 6 carries you a long way. When an app grows complex enough that you want stricter, more traceable state changes, **Fluxor** brings a Redux-style store (actions, reducers, a single state tree) to Blazor. Reach for it when "where did this value change?" stops being obvious.

## What to build next

Reading more won't make this stick. Building one real thing will - here's the assignment, deliberately concrete.

Build a small **CRUD app** - create, read, update, delete - end to end:

- A **Blazor UI** for listing, adding, editing, and deleting records (you already have every piece: components, forms, validation, `HttpClient`).
- An **ASP.NET Core API** behind it to serve and persist the data, with **EF Core** talking to a database. Blazor and ASP.NET Core are designed to pair - [ASP.NET Core From Zero](/guides/aspnet-core-from-zero) is the other half of this stack: the host that serves your app and the APIs it calls.
- Add **authentication** so users sign in and see their own data.
- Drop in a **component library** (start with MudBlazor) so it looks finished without you styling every pixel.
- And **choose your render modes on purpose** - a public landing page as static SSR, the authenticated app pages as InteractiveAuto. Make the choice, and be able to explain it.

That single project exercises nearly everything you learned, plus the backend it leans on, and finishing it teaches you more than three more tutorials would.

And remember the throughline that's run under every phase: a Blazor app is a **tree of components that re-render when their state changes**, written in **C#**, and you choose **where that C# runs**. That's it. That's the whole mental model - and you don't just recognize it now, you build with it. Go ship the CRUD app, deploy it, and show someone. You're ready.

## Recap

1. **You can build interactive web UIs in C#** - components and Razor, binding, events and lifecycle, forms and validation, component communication and shared state, and calling APIs with injected `HttpClient`. That's a real front-end skill, not a toy.
2. **Blazor vs the JS frameworks is about fit, not winners** - Blazor shines for .NET teams and line-of-business apps (shared C# language, types, tooling); React/Vue/Angular win on ecosystem size and for heavy public SPAs where WASM's larger initial download hurts.
3. **Render modes are a per-page choice (.NET 8)** - static SSR, InteractiveServer (SignalR), InteractiveWebAssembly, and InteractiveAuto (Server first, then WASM). Pick deliberately along the download-size vs latency vs offline tradeoff.
4. **Lean on libraries** - component sets like MudBlazor, Radzen, Fluent UI, and Telerik for ready-made UI; the DI state-service pattern for state, with Fluxor (Redux-style) when an app gets big.
5. **Build one CRUD app and finish it** - Blazor UI on an ASP.NET Core + EF Core backend, add auth, add a component library, choose render modes on purpose. That project cements the whole guide.

## Quick check

Three decisions to take with you as you leave this guide:

```quiz
[
  {
    "q": "A .NET team is building an internal line-of-business app and wants to share their model classes and validation between server and UI. Which choice fits the reasoning best?",
    "choices": [
      "React, because it always has a smaller bundle",
      "Blazor, because the UI is C# and shares language, types, and tooling with the backend",
      "Angular, because line-of-business apps require TypeScript",
      "It makes no difference; all frameworks are interchangeable"
    ],
    "answer": 1,
    "explain": "Blazor's big win for a .NET team is building the UI in C#, sharing the same language, model types, and tooling with the backend. For a heavy public consumer SPA or a JS-centric team, a JS framework may fit better instead."
  },
  {
    "q": "You want fast first-load startup, but also offline capability and no per-click server round-trips on later visits. Which .NET 8 render mode is designed for exactly that?",
    "choices": [
      "Static SSR",
      "InteractiveServer",
      "InteractiveAuto",
      "There is no mode that does both"
    ],
    "answer": 2,
    "explain": "InteractiveAuto uses Server for the first load (fast startup) and switches to WebAssembly afterward once the runtime is cached, giving you quick startup plus offline-capable, round-trip-free interactivity later."
  },
  {
    "q": "You want ready-made, styled UI components (data grids, date pickers, dialogs) instead of hand-building them in Blazor. What do you reach for?",
    "choices": [
      "Fluxor",
      "A component library like MudBlazor, Radzen, or Fluent UI Blazor",
      "SignalR",
      "EditForm"
    ],
    "answer": 1,
    "explain": "Component libraries (MudBlazor, Radzen, Fluent UI Blazor, Telerik) give you ready-made UI components. Fluxor is for Redux-style state management, not UI widgets."
  }
]
```
