Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
What Is Blazor? The Blazor Web App, Render Modes and When to Choose It over Angular or MVC
Blazor lets you write interactive web UI with C# and Razor components, and since .NET 8 a render mode decides where each component runs: Static SSR, Interactive Server, Interactive WebAssembly or Auto. We run the same component in all four modes side by side and show the prerendered output, the SignalR circuit, the 2.2 MB downloaded on the first visit and the Blazor-versus-Angular decision for Bookstore's admin panel with real measurements.
Your First Blazor Web App: Template Options, Project Structure, App.razor, Routes.razor and Hot Reload
The --interactivity option of dotnet new blazor puts the code that runs in the browser into a separate .Client project for WebAssembly or Auto; two registrations in Program.cs and MapRazorComponents<App> produce the endpoints, and App.razor and Routes.razor set up the page skeleton and routing. We add Bookstore.Admin to Bookstore.slnx with central package management and show route parameters and constraints, hot reload with dotnet watch and the .NET 11 template changes with real output.
Razor Component Fundamentals: Parameters, Event Handling, Data Binding and the Component Lifecycle
A Razor component receives values through [Parameter], notifies its parent with EventCallback, binds to inputs with @bind, and ComponentBase calls its lifecycle methods in a fixed order; with prerendering on, OnInitializedAsync runs twice. We build the Bookstore admin panel's stock screen with BookList, BookRow and LowStockBadge and show @bind:after, @bind:get/set, rendering in async handlers and a timer that needs InvokeAsync with real logs and Angular equivalents.
Render Modes in Depth: Static SSR, InteractiveServer, InteractiveWebAssembly, InteractiveAuto and Prerendering
A render mode is set on the component definition, where it is used or for the whole app, propagates to child components, and an interactive component cannot switch to another interactive mode inside; parameters from a Static parent are serialized to JSON in the HTML. We add a WebAssembly quote calculator to the Bookstore panel and show the ChildContent error, a silently dropped EventCallback, turning prerendering off and why everything in .Client is visible in the browser.
Blazor Without Interactivity: Static SSR, Streaming Rendering, Enhanced Navigation and Form Posts
With Static SSR a Blazor component is rendered on the server per request and sent as plain HTML; interaction is built with links, query strings and form posts, and [StreamRendering] sends the skeleton immediately and appends the data to the same response. We show book search and paging in the Bookstore panel, enhanced navigation, an antiforgery-protected stock count form, a 404 with NotFound() from .NET 10 and the TempData coming in .NET 11 with real output.
Prerendering and State Persistence: the Double-Load Problem, [PersistentState] and Circuit State
An interactive component with prerendering runs as two instances, so its data is loaded twice; .NET 10's [PersistentState] embeds the prerendered value in the page and hands it to the interactive instance, and the same attribute keeps state when a circuit pauses and resumes. In the Bookstore panel we measure and remove the double API call and the table flash, use RestoreBehavior for data that must stay fresh, and show that state persisted for WebAssembly is plain JSON in the page source.
Forms and Validation in Blazor: EditForm, Input Components, DataAnnotations and .NET 10 Nested Validation
A Blazor form is built with EditForm and EditContext, and its rules sit on the model as DataAnnotations attributes; in .NET 10, AddValidation and [ValidatableType] also validate nested objects and lists. In the Bookstore panel we show a form that adds an author with their books, a custom attribute for the ISBN check digit, mapping the API's 400 errors to the right field with ValidationMessageStore, and when a Static SSR form is validated, with real output.
Component Design: RenderFragment, Templated and Generic Components, CascadingValue, CSS Isolation and bUnit
Reusable Blazor components are built with RenderFragment and @typeparam; @key ties row state to the right item, ShouldRender cuts needless renders, and cascading values don't cross render mode boundaries. In the Bookstore panel we show a generic DataTable, an input that moves to the wrong row without @key, renders dropping from 42 to 4, a cascading value that is null in WebAssembly, QuickGrid, a Virtualize measurement, ::deep and bUnit tests, with real output.
DI, State and Data Access in Blazor: Service Lifetimes, Circuit Scope, DbContext and a Shared Abstraction for Auto Mode
In Blazor a Scoped service lives for the circuit on InteractiveServer and for the tab on WebAssembly, so on the circuit DbContext is used through IDbContextFactory or OwningComponentBase, and in Auto mode data access is solved with two implementations of one interface. In the Bookstore panel we show a quote shared through a Singleton, stale data from a circuit-long context, the Bookstore.Data decision, IBookCatalog and the .NET 10 WebAssembly response streaming change, with real output.
JavaScript Interop in Blazor: IJSRuntime, Collocated JS Modules, DotNetObjectReference and What's New in .NET 10
In Blazor, JS is imported as a .razor.js module next to the component and called through an IJSObjectReference; calls from JS back to .NET use DotNetObjectReference and [JSInvokable], and since there is no JS during prerendering, calls start in OnAfterRenderAsync. In the Bookstore panel we build a Chart.js sales chart with .NET 10's InvokeConstructorAsync, GetValueAsync and SetValueAsync, and show an ISBN copy button, JSDisconnectedException and the 32 KB message limit with real output.
Authentication and Authorization in Blazor: AuthenticationStateProvider, AuthorizeView, Auth per Render Mode and Passkeys
In Blazor the user comes from AuthenticationStateProvider: in InteractiveServer it is read from the cookie when the circuit starts and stays fixed, while WebAssembly receives only the serialized name and role claims, so AuthorizeView is a UI decision and security belongs to the endpoint and the API. In the Bookstore panel: Identity sign-in, a fallback policy, role-based price editing, an API protected by panel-signed tokens, and the passkey support in the .NET 10 template.
Taking Blazor to Production: WASM Size, Trimming, AOT, Circuit Resources, Reconnection UI and Hosting
Taking Blazor to production means measuring two costs: the first-visit download in WebAssembly (2.3 MB for the Bookstore panel, 4.3 MB with AOT but about six times faster on CPU work) and memory per circuit on the server (about 800 KB on the stock page, with a closed tab kept for three minutes). The post covers trimming and relinking, fingerprinted caching, CircuitOptions, ReconnectModal, a nonce-based Content Security Policy and WebSockets behind Nginx, all with real measurements.