102 Blazor Interview Questions and Answers (2026)

Blazor has quietly become a must-know for .NET developers, and interviewers are raising the bar. They no longer want to hear that you've "heard of" render modes or component lifecycle, they want to see you actually reason through hosting models, state, and JS interop. Walk in shaky on those and a stronger candidate takes the offer.
This guide gives you 102 questions with concise, interview-ready answers and code where it helps. It's worked from Junior to Mid to Senior, so you start with fundamentals and build up to the deep stuff: prerendering, state persistence, performance, and static SSR. Work through it and you'll walk in ready to prove you know Blazor cold.
Q1.What is the difference between a component and a page in Blazor?
Every page in Blazor is a component, but not every component is a page: a page is simply a routable component that has been assigned a URL via the @page directive.
Component:
A reusable, self-contained piece of UI (a .razor file) with its own markup, logic, and lifecycle.
Rendered by being placed inside another component's markup (e.g. <Counter />), often receiving data via [Parameter].
Page:
A component with a route template, so the router can navigate to it directly by URL.
Declared with @page "/counter"; it can also read route parameters and query strings.
Key takeaway: There is no separate "Page" type: a page is just a component that opted into routing. Both are compiled to classes deriving from ComponentBase.
Q2.What is Blazor, and what problem does it solve compared to JavaScript SPA frameworks like React or Angular?
Blazor is a .NET UI framework for building interactive web apps using C# and Razor instead of JavaScript. It lets .NET developers write full-stack web apps in one language and share code (models, validation) between client and server, solving the problem of maintaining a separate JavaScript stack alongside a .NET backend.
One language, one ecosystem: Write UI, logic, and backend in C#; reuse the same DTOs and validation on both sides instead of duplicating them in TypeScript.
Component model like React/Angular: Same mental model (composable components, data binding, lifecycle), so concepts transfer, but expressed in Razor + C#.
Multiple hosting models:
Blazor WebAssembly runs C# in the browser via a WASM .NET runtime (no JS SPA needed).
Blazor Server runs logic on the server and streams UI updates over a SignalR connection.
Trade-offs vs JS frameworks: Larger initial download (WASM runtime) and a smaller ecosystem than npm; JS interop is still needed for many browser APIs and JS libraries.
Q3.What is a Razor component, and what does a .razor file contain?
.razor file contain?A Razor component is the fundamental building block of a Blazor UI: a reusable unit combining HTML markup and C# logic. It lives in a .razor file that the compiler turns into a C# class deriving from ComponentBase.
A .razor file typically contains:
Directives at the top (@page, @using, @inject, etc.).
Razor markup: HTML mixed with C# expressions via @ (e.g. @count, @if, @foreach).
An @code block holding fields, properties, parameters, and methods.
Naming and compilation: The file name becomes the component/tag name (Counter.razor → <Counter />), so it must start with an uppercase letter.
Q4.What does the @page directive do, and how do route templates map URLs to components?
@page directive do, and how do route templates map URLs to components?The @page directive turns a component into a routable page by registering a route template with the Blazor router. When the URL matches the template, the router renders that component.
How it works:
At compile time @page "/counter" adds a [Route] attribute; the Router component scans the assembly for these and matches the current URL.
A component can have multiple @page directives to answer more than one URL.
Route parameters:
Curly braces capture segments: @page "/user/{Id}" binds to a matching [Parameter] property.
Add type constraints ({Id:int}) and catch-all segments ({*path}) as needed.
Optional and query: Optional parameters use {Id?}; query strings can be bound with [SupplyParameterFromQuery].
Q5.What are the common directives (@inject, @using, @inherits, @implements, @namespace, @attribute) and what does each do?
@inject, @using, @inherits, @implements, @namespace, @attribute) and what does each do?Razor directives configure how a component is compiled and what it has access to. These common ones handle dependency injection, namespace imports, inheritance, interfaces, and applying attributes to the generated class.
@inject: Injects a service from DI into a property, e.g. @inject NavigationManager Nav.
@using: Imports a namespace, just like a C# using; put shared ones in _Imports.razor.
@inherits: Sets a custom base class instead of ComponentBase (e.g. a shared base with common logic).
@implements: Declares that the component implements an interface, e.g. @implements IDisposable to clean up resources.
@namespace: Overrides the generated class's namespace, decoupling it from the folder structure.
@attribute: Applies a .NET attribute to the component class, e.g. @attribute [Authorize].
Q6.What is the difference between OnInitialized and OnParametersSet? When would you use one over the other?
OnInitialized and OnParametersSet? When would you use one over the other?OnInitialized runs exactly once when the component is first created, while OnParametersSet runs after initialization and again every time the parent passes new parameter values. Use OnInitialized for one-time setup and OnParametersSet to react to changing inputs.
OnInitialized: Fires once per component lifetime; ideal for loading data that doesn't depend on parameters changing, wiring up subscriptions, or reading initial config.
OnParametersSet: Fires after every parameter update (including the first), so it's where you recompute derived state or refetch data when a parameter like an Id changes.
The classic trap: If you load data based on a parameter only in OnInitialized, it won't refresh when the parent reuses the same component with a new value; do that in OnParametersSet.
Rule of thumb: Independent of parameters: OnInitialized. Depends on parameters: OnParametersSet, often guarded by comparing the new value to a cached one to avoid redundant work.
Q7.What are the different ways to pass data from a parent component to a child component?
The primary way is component parameters, but Blazor offers several mechanisms depending on whether the data is direct input, shared markup, or shared across a subtree.
Component parameters:
Public properties marked [Parameter] set as attributes: <Child Title="Hi" />.
The most common and explicit path.
Child content: Pass markup between the child's tags via a RenderFragment ChildContent parameter.
Cascading values: Use <CascadingValue> and [CascadingParameter] to flow data down a subtree without threading it through every level.
Attribute splatting: Forward extra attributes via CaptureUnmatchedValues and @attributes.
Shared services (indirect): An injected state container/service both components read, useful when the relationship isn't strictly parent-to-child.
Q8.What are the three primary ways to pass data between components in Blazor (Parameters, Cascading Values, and State Services)?
Blazor offers three main ways to move data between components, each for a different relationship: [Parameter] for direct parent-to-child, CascadingValue for a whole subtree, and DI state services for components that aren't related in the tree.
Parameters:
Public properties marked [Parameter], set explicitly by the immediate parent's markup.
Explicit and easy to trace; child-to-parent communication uses EventCallback.
Cascading values:
Provided by CascadingValue and consumed anywhere below with [CascadingParameter].
Avoids parameter drilling for data many descendants need (theme, auth).
State services:
Injected classes registered in DI that hold shared data and raise change events.
The only clean option for unrelated components or app-wide/persistent state.
Choosing: Direct child: parameters. Shared subtree: cascading. No tree relationship or global: state service.
Q9.How does the Blazor Router component decide which component to render?
Router component decide which component to render?The Router component scans the assemblies you specify for components decorated with @page directives, builds a route table, and matches the current URL against it to pick the component to render.
Route table construction: AppAssembly (and AdditionalAssemblies) are reflected for RouteAttribute entries generated from @page.
Matching the URL:
On navigation the router matches the path, applies route constraints, and renders inside <Found> via RouteView.
Literal routes are preferred over parameterized ones when both match.
No match: The <NotFound> template renders instead.
Layout application: RouteView wraps the matched component in its DefaultLayout (unless the page specifies its own).
Q10.What is the difference between OnValidSubmit and OnSubmit in Blazor forms?
OnValidSubmit and OnSubmit in Blazor forms?Both are EditForm submit callbacks, but they differ in who owns validation: OnValidSubmit fires only after validation passes, while OnSubmit fires on every submit and hands you the full validation responsibility.
OnValidSubmit (and OnInvalidSubmit):
Blazor runs validation first; OnValidSubmit runs only if the model is valid, OnInvalidSubmit only if it isn't.
Use this for the common case: let the framework validate, you just handle success.
OnSubmit:
Fires on every submit regardless of validity; you must call editContext.Validate() yourself.
Use it when you need full control over the validation flow.
Mutually exclusive: Provide either OnSubmit or the OnValidSubmit/OnInvalidSubmit pair, not both.
Q11.What are the built-in input components in Blazor forms (InputText, InputNumber, InputSelect, InputCheckbox, InputDate), and how do they integrate with EditForm?
InputText, InputNumber, InputSelect, InputCheckbox, InputDate), and how do they integrate with EditForm?Blazor ships strongly-typed input components that wrap standard HTML inputs and integrate with the form's EditContext through two-way binding and automatic field tracking. You bind them with @bind-Value to a model property.
The built-in components:
InputText and InputTextArea for strings.
InputNumber for numeric types.
InputSelect for dropdowns (enums, lists).
InputCheckbox for booleans.
InputDate and InputRadioGroup for dates and radio sets.
How they integrate:
They receive the EditContext as a cascading parameter (all derive from InputBase<T>).
On value change they notify the context via NotifyFieldChanged, marking the field modified and triggering validation.
They automatically apply CSS classes (modified, valid, invalid) reflecting field state.
Q12.How does Blazor communicate with JavaScript, and what is the difference between InvokeAsync and InvokeVoidAsync?
InvokeAsync and InvokeVoidAsync?Blazor talks to JavaScript through the IJSRuntime abstraction (JS interop): InvokeAsync<T> calls a JS function and returns its result, while InvokeVoidAsync calls one when you don't need a return value.
The interop channel:
Inject IJSRuntime; arguments and results are JSON-serialized, so pass serializable data.
In Blazor Server the calls cross the SignalR circuit (network latency), so interop is always async.
InvokeAsync<T>: Use when JS returns a value; T is the deserialized return type (e.g. InvokeAsync<string>).
InvokeVoidAsync: Use for fire-and-forget-style calls with no result (e.g. localStorage.setItem, focusing an element); it's just the void-returning convenience overload.
Timing caveat: Interop needs a rendered DOM, so avoid it in OnInitialized; do it in OnAfterRenderAsync.
Q13.How do layouts work in Blazor, and what are the roles of @layout, LayoutComponentBase, and the @Body placeholder?
@layout, LayoutComponentBase, and the @Body placeholder?Layouts let you define shared shell markup (nav, header, footer) once and wrap page content inside it: a layout is a component that inherits LayoutComponentBase and renders its page via @Body, while pages opt in with @layout.
LayoutComponentBase: The base class every layout inherits; it supplies a Body parameter of type RenderFragment.
@Body: The placeholder in the layout markup where the routed page's content is rendered.
@layout: A directive placed in a page (or in _Imports.razor for a whole folder) to select which layout wraps it.
Applying layouts globally: The Router's RouteView takes a DefaultLayout, so pages without an explicit @layout still get one.
Nesting: A layout can itself declare @layout to nest inside another layout.
Q14.What is ComponentBase, and what functionality does it provide to a Razor component?
ComponentBase, and what functionality does it provide to a Razor component?ComponentBase is the default base class every Razor component inherits from. It supplies the rendering machinery and lifecycle methods that make a class behave as a Blazor component.
Rendering and state: Provides StateHasChanged() to flag the component for re-render, and manages the render tree diffing.
Lifecycle methods: Virtual hooks you override: OnInitialized/OnInitializedAsync, OnParametersSet, OnAfterRender, and ShouldRender.
Parameter handling: Implements SetParametersAsync, which binds incoming [Parameter] values before lifecycle events run.
Event dispatch: Marshals UI event callbacks onto the synchronization context and triggers re-rendering automatically after handlers run.
Note: You can inherit a different base with @inherits, but it should ultimately derive from ComponentBase (or implement IComponent).
Q15.What is the difference between writing your C# logic in an @code block versus using a code-behind partial class?
@code block versus using a code-behind partial class?Both approaches produce the same compiled class (the .razor file and its partial class are merged); the difference is organization. An @code block keeps logic inline with the markup, while a code-behind partial class separates C# into its own .razor.cs file.
@code block (inline):
Simple and colocated: good for small components where markup and logic are read together.
Can clutter the file as logic grows.
Code-behind partial class:
Declared as public partial class Counter in Counter.razor.cs; the markup file stays purely presentational.
Better for complex components: full IDE tooling, easier unit testing, cleaner separation, and injection via [Inject] attributes instead of @inject.
Bottom line: Functionally identical; choose inline for small components and code-behind for larger ones or team consistency.
Q16.What is a Razor Class Library (RCL), and how do you use it to package and share reusable components and static assets?
A Razor Class Library (RCL) is a .NET project type designed to package reusable Razor components, along with their static assets (CSS, JS, images), so they can be shared across multiple Blazor apps via a project reference or NuGet package.
Creating one: Use dotnet new razorclasslib; it produces a library targeting Razor components rather than an executable app.
Consuming components: Reference the RCL, then add its namespace via @using MyLib (often in _Imports.razor) and use the components like any local ones.
Static assets:
Files under wwwroot are exposed at the special path _content/{LibraryName}/... so consuming apps can link them.
Supports CSS isolation and collocated JS modules per component.
Why use it: Promotes reuse of a design system or component set across projects and clean distribution through NuGet.
Q17.What is Static Server-Side Rendering (Static SSR) in Blazor, and how does it differ from Blazor Server?
Static SSR) in Blazor, and how does it differ from Blazor Server?Static SSR renders a component to HTML on the server for a single request and sends plain markup to the browser with no interactivity: no SignalR circuit and no persistent connection. Blazor Server, by contrast, keeps a live circuit so the same component stays interactive after the initial render.
Static SSR is request/response HTML:
The component runs once to produce HTML, then the connection ends: like classic server-rendered pages (MVC/Razor Pages).
No @onclick or state that reacts on the client; interactivity requires forms/navigation or enhanced navigation.
Blazor Server is stateful and interactive: Maintains a SignalR circuit and in-memory component state, so events re-render and push diffs back.
Cost and use cases:
Static SSR is cheapest and most scalable (no per-user memory), great for SEO and content pages.
Blazor Server costs a persistent connection and server memory per user.
In .NET 8 you can mix them: start Static SSR and opt specific components into interactivity via a render mode.
Q18.What is the difference between the four new render modes in .NET 8 (Static SSR, Interactive Server, Interactive WebAssembly, and Interactive Auto)?
.NET 8 (Static SSR, Interactive Server, Interactive WebAssembly, and Interactive Auto)?The four modes differ along two axes: whether the component is interactive at all, and if so, where its interactive code runs (server via SignalR, client via WebAssembly, or automatically chosen). In .NET 8 they can be applied per component or per page.
Static SSR: Renders HTML once, no interactivity, no persistent connection: fast and highly scalable.
Interactive Server (InteractiveServer): Runs on the server over a SignalR circuit: instant startup, but each interaction is a round-trip and holds server state.
Interactive WebAssembly (InteractiveWebAssembly): Runs in the browser on the WASM runtime: local low-latency UI and offline capability, but larger initial download.
Interactive Auto (InteractiveAuto): Starts on the server for a fast first load, then switches to WebAssembly once assets are cached: combines quick startup with client-side performance.
Key selection points: Use Static SSR for content/SEO, Interactive Server for quick interactivity with server resources, WebAssembly for rich offline clients, Auto for the best overall UX.
Q19.How does Blazor Hybrid differ from Blazor WebAssembly, and what is the role of the BlazorWebView?
Blazor Hybrid differ from Blazor WebAssembly, and what is the role of the BlazorWebView?Blazor Hybrid runs your Razor components natively inside a desktop or mobile app using the local .NET runtime, rendering the UI into an embedded BlazorWebView control, whereas Blazor WebAssembly runs the components inside a browser sandbox on the WASM runtime. Hybrid is a native app; WASM is a web app.
Execution environment:
Hybrid: components run on the full native .NET runtime (via MAUI, WPF, or WinForms), not compiled to WebAssembly.
WASM: components run in the browser's sandbox on the Mono/WASM runtime.
Role of BlazorWebView:
It's a native control that hosts a system WebView and renders the Blazor UI as HTML inside it.
There is no SignalR and no download of a .NET runtime: it just displays the rendered output of local components.
Capabilities:
Hybrid has full native access (file system, sensors, native APIs) and no browser sandbox restrictions.
WASM is limited to what the browser allows.
Distribution: Hybrid ships as an installable app through app stores; WASM is served over the web via a URL.
Q20.When would you choose Blazor Hybrid (using MAUI or WPF) over a standard Blazor WebAssembly PWA?
Blazor Hybrid (using MAUI or WPF) over a standard Blazor WebAssembly PWA?Choose Blazor Hybrid when you need native device access, full .NET runtime performance, or an installable store-distributed app, and are willing to trade the browser's zero-install reach. Pick a WASM PWA when broad, install-free web reach and easy updates matter most.
Native device integration: Need file system, Bluetooth, sensors, notifications, or other OS APIs beyond the browser sandbox.
Performance & runtime: Full native .NET runs faster than WASM and avoids the initial runtime download.
Offline & distribution: You want an app-store presence and a true installed desktop/mobile app rather than a website.
Code reuse: You can share the same Razor components across web and native targets while going native where it counts.
When WASM PWA wins instead: Maximum reach with no install, instant updates via redeploy, and cross-platform by URL: prefer WASM when native APIs aren't required.
Q21.What is a Circuit in Blazor Server and how is its lifetime managed?
Circuit in Blazor Server and how is its lifetime managed?A circuit is the server-side session for a Blazor Server user: the live SignalR connection plus the in-memory component tree, state, and DI-scoped services that keep the UI interactive. It exists as long as the connection lives (with a grace period), and its state is lost when it's disposed.
What a circuit holds: The rendered component instances, their fields/state, and the Scoped services for that user's session.
Creation: Established when the browser opens the SignalR connection after the initial page load.
Temporary disconnection: If the connection drops briefly, the server retains the circuit for a configurable window so it can reconnect and resume with state intact.
Disposal: After the retention timeout or on explicit teardown, the circuit is disposed and all in-memory state is gone.
Implications:
Circuits consume server memory per user, so they affect scalability; don't store sensitive/large data indefinitely.
You can hook CircuitHandler to react to connection/disconnection lifecycle events.
Q22.When should you use OnAfterRender instead of OnInitialized, and why is it the only safe place to perform JavaScript interop?
OnAfterRender instead of OnInitialized, and why is it the only safe place to perform JavaScript interop?Use OnAfterRender for work that needs the DOM to actually exist (JS interop, focusing elements, initializing JS widgets), whereas OnInitialized runs before any rendering. It's the only safe place for JS interop because the rendered DOM and, in Server, the SignalR connection to the browser are only guaranteed to be ready after the first render.
Why not OnInitialized for interop:
In Server prerendering there's no live JS runtime/circuit yet, so calling IJSRuntime throws or fails.
The DOM elements you want to touch haven't been rendered.
What OnAfterRender guarantees: The component has rendered to the DOM and the client connection is established, so interop is valid.
Use the firstRender flag: One-time setup (initialize a chart, set focus) should be guarded by if (firstRender) so it doesn't repeat on every render.
Don't set state carelessly here: Calling StateHasChanged in OnAfterRender can cause re-render loops; only do it when necessary.
Q23.Walk through the standard Blazor component lifecycle, explaining the order of SetParametersAsync, OnInitialized, OnParametersSet, and OnAfterRender.
SetParametersAsync, OnInitialized, OnParametersSet, and OnAfterRender.When a component is created or its parameters change, Blazor runs a fixed sequence: parameters are set, then first-time initialization, then a parameters-set hook, a render, and finally an after-render hook once the DOM exists.
SetParametersAsync: Receives the incoming ParameterView and assigns values to [Parameter] properties; runs first, every time parameters arrive. Override rarely.
OnInitialized / OnInitializedAsync: Runs once in the component's lifetime, after parameters are first set; ideal for one-time setup and initial data loads.
OnParametersSet / OnParametersSetAsync: Runs after initialization and again every time a parent supplies new parameters; react to changed inputs here.
Render (BuildRenderTree): The component renders after the above; ShouldRender can suppress re-renders.
OnAfterRender / OnAfterRenderAsync: Runs after the DOM is updated, with firstRender true on the first pass; the safe spot for JS interop.
Note on async hooks: An await in the async versions lets Blazor render an intermediate state before the task completes, so the UI can show a loading state.
Q24.What is the difference between OnInitialized and OnInitializedAsync, and how does the renderer handle the component while the async task is still running?
OnInitialized and OnInitializedAsync, and how does the renderer handle the component while the async task is still running?Both run during component initialization, but OnInitialized is synchronous while OnInitializedAsync returns a Task the renderer can await: the component renders once before the async work completes, then again when it finishes.
OnInitialized (synchronous): Runs first, meant for quick setup that returns immediately.
OnInitializedAsync (asynchronous):
Meant for awaited I/O like fetching data with await http.GetAsync(...).
Both run each time the component is initialized (only once per instance).
How the renderer handles the pending task:
At the first await that yields, the renderer renders the component immediately with whatever state exists, so you can show a loading placeholder.
When the Task completes, the framework automatically calls StateHasChanged and re-renders with the final data.
Practical implication: null-guard your markup, since it may render before the awaited data arrives.
Q25.What triggers a Blazor component to re-render, and what changes are NOT automatically detected by the framework?
Blazor re-renders a component when its own lifecycle or events signal a state change, but it does NOT watch your fields for mutation: changes that happen outside its known triggers require a manual StateHasChanged.
What triggers a re-render automatically:
UI events wired through the framework (@onclick, @onchange, etc.).
Completion of lifecycle async tasks (OnInitializedAsync, OnParametersSetAsync).
Receiving new parameters from a parent render.
An explicit call to StateHasChanged.
What is NOT detected:
State mutated from outside a UI event: timer callbacks, background threads, or event handlers from non-Blazor code.
Mutating a collection or object property without any triggering event: Blazor has no change tracking on your data.
Updates raised on another thread: you must marshal with InvokeAsync(StateHasChanged).
Note: reference vs value equality on [Parameter] values determines whether a child re-renders when the parent renders.
Q26.What is the purpose of StateHasChanged(), and when does the framework call it automatically vs when must you call it manually?
StateHasChanged(), and when does the framework call it automatically vs when must you call it manually?StateHasChanged tells Blazor to queue the component for re-rendering by re-running its render logic and diffing the result. The framework calls it for you after known triggers, but you must call it yourself when state changes outside those triggers.
What it actually does:
Marks the component dirty and schedules a render (it does not render synchronously and immediately in all cases).
On render it builds a new render tree and diffs it against the previous one, applying only the differences.
Called automatically after:
Framework-wired UI event handlers.
Lifecycle methods completing (including async continuations).
Parameter changes from a parent.
Call it manually when:
State changes from a timer, background task, or external event outside Blazor's flow.
From another thread, wrap it: await InvokeAsync(StateHasChanged) to run on the renderer's synchronization context.
Q27.Explain the difference between EventCallback and a standard C# Action or EventHandler, and why EventCallback is preferred for child-to-parent communication.
EventCallback and a standard C# Action or EventHandler, and why EventCallback is preferred for child-to-parent communication.An EventCallback is Blazor's built-in delegate type for component events: unlike a plain Action or EventHandler, it is bound to a receiver, dispatches on the correct synchronization context, and automatically triggers a re-render of the parent.
Automatic re-render: Invoking an EventCallback calls StateHasChanged on the component that supplied the handler; a raw Action does not, so the parent's UI may go stale.
Correct context dispatch: It marshals the call onto the receiver's renderer context automatically, avoiding threading issues.
Value type, not reference: EventCallback is a struct, so an unassigned one is safely a no-op rather than a null reference.
Async-friendly: Prefer EventCallback / EventCallback<T> and invoke with InvokeAsync, which awaits async handlers properly.
Q28.Explain RenderFragment and RenderFragment<T>. How do you create a templated component?
RenderFragment and RenderFragment<T>. How do you create a templated component?A RenderFragment is a chunk of UI represented as a delegate the component can render; RenderFragment<T> is a parameterized version that takes a context value, which is the basis for templated components.
RenderFragment: Represents markup with no input; the default child content of a component is a RenderFragment named ChildContent.
RenderFragment<T>:
A function from a value of type T to markup; the value is exposed to the caller as @context (or a named context).
Lets a component render caller-supplied templates for each data item.
Templated component: Define [Parameter] fragments and invoke them per item, passing the item as the context.
Q29.How do you implement two-way binding for a custom component parameter using the Value/ValueChanged pattern?
Value/ValueChanged pattern?Two-way binding on a custom parameter follows a naming convention: expose a Value parameter plus a matching ValueChanged event callback, and Blazor lets the parent write @bind-Value to wire them together.
The convention:
A parameter named Value and an EventCallback<T> named ValueChanged enable @bind-Value.
The name is arbitrary: Foo + FooChanged enables @bind-Foo.
How the loop works:
Parent passes the current value in via Value.
Child invokes ValueChanged.InvokeAsync(newValue), which updates the parent's field and re-renders it.
Optional expression: Add a ValueExpression parameter for validation/form integration support.
Q30.What is the @ref directive, and how do you use it to capture a reference to a child component or DOM element and call its methods?
@ref directive, and how do you use it to capture a reference to a child component or DOM element and call its methods?The @ref directive captures a reference to a rendered child component instance or an element, letting you call its public methods imperatively from the parent's C# code.
Referencing a child component: Add @ref="field" where the field is typed as the component; you can then call its public methods and properties.
Referencing a DOM element: Capture into an ElementReference and pass it to JS interop (e.g. focus, scroll).
Timing matters: The reference is only populated after the component renders, so use it in OnAfterRender / OnAfterRenderAsync, not in OnInitialized.
Use sparingly: Prefer parameters and callbacks for data flow; reserve @ref for imperative actions like focusing an input or invoking a method.
Q31.What does the EditorRequired attribute on a [Parameter] enforce, and when is it checked?
EditorRequired attribute on a [Parameter] enforce, and when is it checked?The [EditorRequired] attribute marks a component parameter as mandatory so consumers get a design-time warning (and a runtime error) if they forget to supply it. It is an authoring/tooling contract, not a null guarantee.
What it enforces:
Signals that a [Parameter] must be provided by whoever uses the component.
Produces a compiler warning (RZ2012) when the parameter is omitted in Razor markup.
When it is checked:
Only when the component is used via Razor markup; it is validated at compile time (tooling) and reported when the component renders.
It is NOT checked if the component is created dynamically (e.g. RenderTreeBuilder or reflection).
Important limitation:
It does not prevent a null from being passed explicitly; it only warns about omission. Still validate critical values yourself.
Must be applied together with [Parameter].
Q32.How does focus management on navigation work with the FocusOnNavigate component?
FocusOnNavigate component?The FocusOnNavigate component automatically moves keyboard focus to a chosen element after each successful routing navigation, which is important for accessibility so screen readers and keyboard users land in the right place when the page changes.
Where it lives: Placed inside the Router (typically in App.razor) so it responds to route changes.
How it selects the target:
Uses a CSS selector via its Selector parameter (commonly h1) to find the element to focus.
After navigation it calls the equivalent of FocusAsync() on the first match.
Why it matters:
In a SPA the page does not reload, so browsers do not reset focus; this restores expected accessibility behavior.
The target should be focusable (a heading gets tabindex="-1" applied automatically to receive focus).
Q33.What is the difference between a [Parameter] and a [CascadingParameter], and when is it better to use a cascading value over parameter drilling?
[Parameter] and a [CascadingParameter], and when is it better to use a cascading value over parameter drilling?A [Parameter] is passed explicitly from a direct parent to its immediate child, while a [CascadingParameter] flows implicitly down the whole component tree from a CascadingValue ancestor. Prefer cascading values when the same data must reach many nested descendants and passing it manually through each layer (prop drilling) would be tedious.
[Parameter]:
Set by name in the parent's markup; explicit and easy to trace.
Only reaches the direct child; deeper components need it re-passed at each level.
[CascadingParameter]:
Received automatically by any descendant wrapped in a CascadingValue, matched by type or by Name.
Great for cross-cutting concerns: themes, auth state (AuthenticationState), the current EditContext.
When cascading beats drilling:
The value is needed by many components at varying depths.
Intermediate components have no interest in the value and shouldn't be forced to relay it.
Trade-offs: Cascading is less explicit (harder to see where a value comes from) and can trigger broader re-renders, so use regular parameters for simple, local data.
Q34.How can you share state between a parent component and deeply nested children without using parameters?
Use a CascadingValue to flow shared state down to deeply nested children without threading it through every intermediate component's parameters. For mutable shared state, cascade an object or a service and let children read and update it.
Cascade an object: Wrap the subtree in <CascadingValue Value="this"> (or a dedicated state object); descendants pick it up with [CascadingParameter].
Make updates propagate: Have the cascaded object expose methods to change state and raise an event; the owning component calls StateHasChanged() to re-render the tree.
Name it when types collide: Use Name on both the CascadingValue and the [CascadingParameter] if multiple values share a type.
When to prefer a service instead: If state must be shared beyond one subtree (across pages/unrelated components), a DI state service is cleaner than cascading.
Q35.How do you share state between two components that are not in a parent-child relationship?
When two components have no parent-child link, share state through a common dependency-injected state service (a singleton or scoped class) that both inject; one updates it and notifies the other via an event so it re-renders.
Create a state container service: A plain class holding the data plus an event Action raised whenever state changes.
Register it in DI: Use AddScoped (per-user in Blazor Server / per-app in WASM) or AddSingleton for app-wide state.
Both components inject it: With @inject; one writes, the other subscribes.
Subscribe and re-render: The reader subscribes in OnInitialized, calls StateHasChanged() in the handler, and unsubscribes in Dispose to avoid leaks.
Q36.How do you handle query string parameters and catch-all routes in Blazor routing?
Query string parameters are read declaratively with [SupplyParameterFromQuery] (or manually parsed from the URL), while catch-all routes use a {*param} segment to capture the remainder of a path into a single string.
Query strings via attribute binding:
Combine [Parameter] with [SupplyParameterFromQuery]; the framework binds ?page=2 automatically.
Use Name to map a differently named query key.
Query strings manually: Parse with QueryHelpers.ParseQuery on NavigationManager.Uri when you need full control.
Catch-all routes:
Declared as @page "/files/{*path}"; path receives the entire remaining segment, including slashes.
Useful for file-browser style URLs or wildcard fallbacks.
Q37.What is the NavigationLock component, and how do you use it to prevent a user from leaving a dirty form?
NavigationLock component, and how do you use it to prevent a user from leaving a dirty form?The NavigationLock component (introduced in .NET 7) declaratively intercepts navigation attempts, letting you cancel internal navigations and warn on external ones, which is ideal for guarding a dirty form.
Two interception points:
ConfirmExternalNavigation: shows the browser's native "leave site?" dialog for URL changes and page unloads.
OnBeforeInternalNavigation: a callback for in-app navigations where you can call context.PreventNavigation().
How to guard a form:
Bind ConfirmExternalNavigation to a bool that tracks whether the form is dirty.
In OnBeforeInternalNavigation, prompt the user (e.g. a modal) and prevent navigation if they cancel.
Advantage over manual interception: Declarative, scoped to the component, and covers both in-app and browser-level exits.
Q38.What is the purpose of NavigationManager, and how do you intercept navigation to prevent a user from leaving a dirty form?
NavigationManager, and how do you intercept navigation to prevent a user from leaving a dirty form?NavigationManager is the injected service for working with URLs programmatically: reading the current address, navigating, and observing/intercepting location changes. To guard a dirty form you register a navigation interceptor with RegisterLocationChangingHandler and cancel the navigation when there are unsaved edits.
Core responsibilities:
Uri and BaseUri expose the current location.
NavigateTo(url) triggers navigation, with options like forceLoad and replace.
Intercepting navigation (.NET 7+):
RegisterLocationChangingHandler gives a handler that runs before navigation completes.
Call context.PreventNavigation() if the form is dirty and the user declines.
Dispose the returned registration to unsubscribe.
Alternative: The NavigationLock component wraps this same mechanism declaratively.
Q39.How do you define route parameters and apply route constraints in a Blazor component?
Route parameters are placeholders in the @page template written as {name} and bound to a matching [Parameter] property; route constraints restrict which values match by appending a type after a colon, e.g. {id:int}.
Defining a parameter: @page "/product/{Id}" maps to a public [Parameter] public string Id { get; set; } (name matched case-insensitively).
Adding constraints:
Supported types include int, bool, datetime, guid, long.
A non-matching value (/product/abc for {id:int}) simply doesn't match that route.
Optional parameters:
Mark with ? as in {id:int?}, or supply multiple @page directives.
Bind optional params to nullable types.
Q40.What is the LocationChanged event on NavigationManager, and how do you subscribe to and clean up from it?
LocationChanged event on NavigationManager, and how do you subscribe to and clean up from it?LocationChanged is an event on NavigationManager that fires after the app's URL has changed, letting you react to navigation (e.g. logging, resetting state). Because it's a plain .NET event, you must unsubscribe in Dispose to avoid memory leaks.
When it fires:
After navigation has already occurred (it does not prevent it: use a location-changing handler or NavigationLock for that).
The handler receives LocationChangedEventArgs with Location and IsNavigationIntercepted.
Subscribe and clean up:
Subscribe with += in OnInitialized.
Implement IDisposable and detach with -= so the component can be garbage collected.
Why cleanup matters: NavigationManager is long-lived; a lingering subscription keeps the component alive indefinitely.
Q41.How do you perform custom validation in a Blazor EditForm that goes beyond Data Annotations?
EditForm that goes beyond Data Annotations?Custom validation beyond Data Annotations is done by working directly with the EditContext and a ValidationMessageStore: you subscribe to validation events, run your own logic, and add messages to fields the store then surfaces in the UI.
Use a ValidationMessageStore:
Create it against the EditContext and add errors with store.Add(fieldIdentifier, message).
Clear it before re-validating so stale messages disappear.
Hook the events:
Handle OnValidationRequested for full-model validation on submit.
Handle OnFieldChanged for per-field validation as the user edits.
Other approaches:
Implement IValidatableObject on the model for cross-field checks that still flow through the annotations validator.
Use a library like FluentValidation with a validator component for complex rule sets.
Q42.How does the new [SupplyParameterFromForm] attribute work in .NET 8 for SSR forms?
[SupplyParameterFromForm] attribute work in .NET 8 for SSR forms?In .NET 8, [SupplyParameterFromForm] binds a component property to data posted from an HTML form during static server-side rendering (SSR), so forms work without interactivity by round-tripping a real HTTP POST.
Model binding for SSR:
Decorate a property with [SupplyParameterFromForm] and Blazor populates it from the posted form values on the next request.
Works without WebSockets or WebAssembly: the browser does a standard form POST.
Pairs with EditForm:
Set the form's FormName so Blazor knows which form on the page was submitted.
The bound handler (OnValidSubmit) runs on the server after the POST.
Anti-forgery: An anti-forgery token is included automatically; the AntiforgeryMiddleware must be in the pipeline.
Q43.What is the relationship between EditForm, EditContext, and FieldIdentifier in the Blazor validation system?
EditForm, EditContext, and FieldIdentifier in the Blazor validation system?They form a layered system: EditForm is the UI container, EditContext is the state/coordination engine, and FieldIdentifier is the key that identifies an individual field within that context.
EditForm:
The component you author with; it creates or accepts an EditContext and cascades it to children.
Handles the submit event and wires up validation callbacks.
EditContext:
Tracks modified state and validation messages, and raises OnFieldChanged and OnValidationRequested.
Bridges the model and the input components.
FieldIdentifier:
A struct pairing a model object with a property name; it uniquely identifies a field.
Used as the key when adding messages to a ValidationMessageStore or querying IsModified().
Q44.What is the role of the DataAnnotationsValidator, ValidationSummary, and ValidationMessage components in an EditForm?
DataAnnotationsValidator, ValidationSummary, and ValidationMessage components in an EditForm?These three components divide validation responsibilities: DataAnnotationsValidator runs the rules, while ValidationSummary and ValidationMessage display the resulting errors at form and field level respectively.
DataAnnotationsValidator:
Placed inside EditForm; subscribes to the context's validation events and validates the model against its [Required], [Range], etc. attributes.
Pushes errors into the context's message store.
ValidationSummary: Renders a consolidated list of all validation messages for the whole form.
ValidationMessage:
Shows messages for a single field, targeted via For="() => model.Property".
Placed next to its input for inline feedback.
Q45.How do you handle file uploads in Blazor using the InputFile component?
InputFile component?The InputFile component exposes selected files through its OnChange event; you read each file as a stream (never trust its raw size) and copy it to a destination, applying explicit size and type limits.
Wire up the component: Handle OnChange with an InputFileChangeEventArgs parameter; use e.File for one file or e.GetMultipleFiles() (add the multiple attribute) for many.
Read via a stream:
Call file.OpenReadStream(maxAllowedSize): the default cap is 512 KB, so pass a larger limit deliberately.
Copy to a FileStream or MemoryStream with CopyToAsync.
Validate on the server: Check file.Size, file.ContentType, and extension; treat all values as untrusted client input.
Server-side caveat: In Blazor Server, file bytes stream over the SignalR circuit, so large uploads are slow and memory-heavy: prefer a dedicated upload endpoint for big files.
Q46.How do you call a .NET method from JavaScript? Explain [JSInvokable] and DotNetObjectReference.
.NET method from JavaScript? Explain [JSInvokable] and DotNetObjectReference.JavaScript calls .NET by targeting methods marked [JSInvokable]: static methods by assembly and name, or instance methods through a DotNetObjectReference you pass to JS so it can invoke back into a specific object.
[JSInvokable] marks the callable surface: Only methods with this attribute can be invoked from JS; you may give an explicit identifier name.
Static invocation: JS calls DotNet.invokeMethodAsync('AssemblyName', 'MethodName', args) with no object context.
Instance invocation with DotNetObjectReference:
Wrap this in DotNetObjectReference.Create(this) and pass it to JS; JS calls ref.invokeMethodAsync('MethodName', args).
This lets JS update component state (e.g. a resize/scroll callback firing back into your component).
Dispose the reference: Implement IDisposable and call Dispose() on the reference to avoid leaking the object.
Q47.What is JS Module Isolation in Blazor, and how do you implement it using IJSObjectReference?
IJSObjectReference?JS module isolation loads a JavaScript file as an ES module scoped to the component that needs it, instead of dumping globals onto window; you import it via IJSRuntime and get back an IJSObjectReference you call functions on.
Why isolate: Avoids global namespace pollution and name collisions; JS is loaded lazily only when the component uses it.
Author an ES module: Use export function in a .js file, typically colocated as Component.razor.js.
Import at runtime: Call JS.InvokeAsync<IJSObjectReference>("import", "./_content/.../file.js") (usually in OnAfterRenderAsync).
Invoke and dispose: Call exported functions via the reference, and call DisposeAsync() (implement IAsyncDisposable) to unload the module.
Q48.How does the AuthorizeView component handle UI-level security, and why must you still implement server-side checks even if you use it?
AuthorizeView component handle UI-level security, and why must you still implement server-side checks even if you use it?AuthorizeView conditionally renders UI based on the user's authentication state, but it only hides markup on the client: it is a convenience, not a security boundary, so real authorization must still be enforced on the server where data lives.
What AuthorizeView does:
Renders <Authorized> content for permitted users and <NotAuthorized> otherwise; supports Roles and Policy parameters.
Exposes the user via a context of type AuthenticationState.
Why it isn't real security:
In WASM, all code and rules run in the browser and can be inspected or bypassed; hidden UI doesn't hide the underlying API.
It controls visibility, not access to data or endpoints.
Enforce on the server:
Protect the APIs the component calls with [Authorize], policies, and server-side role checks.
Rule of thumb: client-side auth improves UX; server-side auth provides the actual guarantee.
Q49.How do you restrict access to a specific Blazor component or page based on roles or policies?
Restrict a page or component with the [Authorize] attribute (specifying Roles or Policy) for routable pages, and use AuthorizeView for finer-grained parts of the markup, all backed by an authorization policy configuration.
Protect a routable page: Add @attribute [Authorize(Roles = "Admin")] or [Authorize(Policy = "CanEdit")] at the top of the .razor file.
Enable routing-level enforcement: Wrap the router with AuthorizeRouteView so unauthorized users hit NotAuthorized content.
Protect part of a component: Use <AuthorizeView Roles="Admin"> or Policy="..." around sensitive markup.
Define policies: Register with AddAuthorizationCore and AddPolicy (e.g. requiring a claim) for logic beyond simple roles.
Reminder: These guard the UI; back them with server-side [Authorize] on the APIs.
Q50.What is the AuthenticationStateProvider, and how do you use it to access the current user's claims?
AuthenticationStateProvider, and how do you use it to access the current user's claims?The AuthenticationStateProvider is the abstraction Blazor uses to obtain the current user's AuthenticationState, which wraps a ClaimsPrincipal. You inject it and call GetAuthenticationStateAsync() to read the user and their claims.
What it provides:
An AuthenticationState object whose User is a ClaimsPrincipal.
From the principal you read User.Identity.IsAuthenticated, User.Identity.Name, and User.Claims.
How to access it: Inject it directly, or preferably receive a Task<AuthenticationState> cascading parameter which integrates with rendering.
Read a specific claim with User.FindFirst("claimType")?.Value.
Q51.Explain the role of the AuthenticationStateProvider. How does it notify the UI that the user's auth status has changed?
AuthenticationStateProvider. How does it notify the UI that the user's auth status has changed?The AuthenticationStateProvider is the service that supplies the current AuthenticationState to components, and it signals auth changes by raising the AuthenticationStateChanged event so subscribed components re-render.
Its role:
Single source of truth for who the user is, exposed via GetAuthenticationStateAsync().
Consumed by AuthorizeView, CascadingAuthenticationState, and [Authorize].
How it notifies the UI:
When state changes (login/logout), the provider calls NotifyAuthenticationStateChanged(...), passing a new Task<AuthenticationState>.
This fires the AuthenticationStateChanged event; CascadingAuthenticationState listens and re-supplies the cascading value, triggering re-render of dependent components.
Custom providers: Subclass AuthenticationStateProvider and call NotifyAuthenticationStateChanged after updating your identity (common in WASM after token refresh or login).
Q52.What are CascadingAuthenticationState and Task<AuthenticationState>, and how do they flow auth state through the component tree?
CascadingAuthenticationState and Task<AuthenticationState>, and how do they flow auth state through the component tree?Together they distribute auth state through the component tree: Task<AuthenticationState> is the cascading value being passed down, and CascadingAuthenticationState is the component that fetches it from the provider and cascades it so any descendant can consume it.
Task<AuthenticationState>:
A cascading parameter representing the (possibly still-loading) auth state, so components can await it and react when it resolves or changes.
Received via [CascadingParameter].
CascadingAuthenticationState:
Wraps part of the tree, subscribes to AuthenticationStateProvider, and supplies the Task<AuthenticationState> to descendants.
Re-cascades a fresh task when the provider raises AuthenticationStateChanged.
How they flow together:
Wrap the app (often in Routes or App) so AuthorizeView and AuthorizeRouteView get the state automatically.
In modern templates it's often added globally, so you may not write CascadingAuthenticationState explicitly.
Q53.How do you maintain application state when a user refreshes the browser in a Blazor WASM app?
A refresh restarts the WASM app and wipes all in-memory state, so you must persist state to somewhere that survives the reload (browser storage or the URL/server) and rehydrate it on startup.
Browser storage:
localStorage survives refresh and browser restarts; sessionStorage survives refresh but not tab close.
Access via a JS interop wrapper or a library like Blazored.LocalStorage.
URL / query string: Good for shareable, bookmarkable state (filters, page numbers) that should survive refresh naturally.
Server-backed state: For anything authoritative or large, refetch from an API on startup using an identifier held in storage or the URL.
Rehydration pattern: On OnInitializedAsync, read the persisted value and repopulate your state container / service so components render with the restored data.
Caveat: Never store secrets or trusted values (tokens, prices) unencrypted in client storage; treat them as tamperable.
Q54.How do you persist small pieces of state in the browser using Protected Browser Storage or local/session storage?
Protected Browser Storage or local/session storage?For small values you use browser localStorage/sessionStorage via JS interop; on Blazor Server, ProtectedLocalStorage and ProtectedSessionStorage add encryption via the Data Protection API so stored values can't be read or tampered with by the client.
Protected Browser Storage (Server only):
Inject ProtectedLocalStorage or ProtectedSessionStorage.
Use SetAsync(key, value), GetAsync<T>(key), DeleteAsync(key); payload is encrypted and integrity-protected.
Encryption keys live on the server, so this is unavailable to pure WASM.
Plain storage (WASM or Server):
Use JS interop or Blazored.LocalStorage; values are plain text and readable/editable by the user.
local vs session: local persists across restarts, session clears when the tab closes.
Timing caveat: JS interop can't run during prerendering (no JS runtime yet), so read storage in OnAfterRenderAsync or after interactivity starts.
Best for: Small, non-critical data: theme, UI preferences, draft form input, last-viewed id.
Q55.What is the <Virtualize> component, and how does it improve performance for large datasets?
<Virtualize> component, and how does it improve performance for large datasets?<Virtualize> is a built-in component that renders only the rows currently visible in the viewport (plus a small buffer) instead of the entire collection, dramatically reducing DOM nodes and render work for large lists.
How it works:
It measures row height and the scroll container, then renders only the items in view.
Uses spacer elements above and below to preserve the correct scrollbar size and position.
Two data modes:
Items: bind an in-memory collection directly.
ItemsProvider: a callback that fetches just the requested slice (start + count) on demand, ideal for paging from a server/DB without loading everything.
Useful parameters:
ItemSize hints row height for accurate scroll math; OverscanCount renders extra buffer rows to smooth scrolling.
Provide a Placeholder template shown while ItemsProvider data loads.
Caveats:
Works best with roughly uniform row heights; wildly variable heights hurt scroll accuracy.
Needs a scrollable container with a fixed/known height.
Q56.Explain how CSS Isolation works in Blazor. What does the ::deep combinator do?
::deep combinator do?CSS isolation scopes a component's styles to that component only, by writing a .razor.css file alongside it; Blazor rewrites those rules with a unique attribute so they can't leak to or from other components.
How it works:
At build time Blazor generates a unique attribute (e.g. b-abc123) and stamps it on the component's elements.
Each selector is rewritten to include [b-abc123], and all rules are bundled into a single {Project}.styles.css file referenced once.
The scoping boundary: Styles apply only to that component's own markup, not to child components it renders.
The ::deep combinator:
It lets a scoped rule reach into child content: the scope attribute is applied to the ancestor, and ::deep makes descendants match.
Useful for styling markup passed as ChildContent or rendered by a child component.
Q57.How do you implement global error handling in Blazor using ErrorBoundary?
ErrorBoundary?Wrap regions of your UI in the built-in <ErrorBoundary> component: it catches unhandled exceptions from its child components and renders fallback UI instead of tearing down the whole app (or, in Blazor Server, killing the circuit).
Basic usage:
Provide ChildContent (the guarded UI) and ErrorContent (the fallback shown on error).
Common pattern: wrap @Body in MainLayout for app-wide coverage.
Recovery: Once triggered it stays in the error state; call Recover() (e.g. on navigation) to reset it.
Custom logic: Subclass ErrorBoundary and override OnErrorAsync to log the exception centrally.
Limits: It only catches errors raised during a component's lifecycle/render; it won't catch background async void or detached tasks.
Q58.What is the conceptual difference between testing a Blazor component with bUnit vs. a standard unit test?
bUnit vs. a standard unit test?A standard unit test exercises a class's methods in isolation with no rendering; bUnit renders the component in a simulated Blazor environment so you can assert on the produced markup and lifecycle behavior, effectively a component/integration test.
Standard unit test:
Treats the component (or a service) as a plain object; calls methods and checks return values or state.
Cannot see rendered output, parameter binding, or event callbacks.
bUnit test:
Uses a TestContext to render markup, then queries the DOM and simulates events like click.
Verifies the full render pipeline: parameters, EventCallback, re-renders after state changes, and cascading values.
Supports injecting mock services and stubbing child components.
Rule of thumb: Test pure logic with plain unit tests; use bUnit when the behavior depends on rendering and user interaction.
Q59.How does two-way data binding work in Blazor, and what is the underlying Value / ValueChanged pattern behind @bind?
Value / ValueChanged pattern behind @bind?Two-way binding in Blazor is compiler sugar: @bind expands into a value assignment plus an event handler, following the Value / ValueChanged pattern so the UI and the field stay in sync.
What the compiler generates: @bind="name" becomes value="name" plus an onchange handler that writes the input back into name.
The component-level pattern:
A parameter (e.g. Value) plus a matching EventCallback named ValueChanged lets @bind-Value work on your own component.
The name is a convention: X and XChanged enable @bind-X.
Change timing: By default it updates on the onchange DOM event (on blur); use @bind:event="oninput" to update on every keystroke.
Q60.What do the @bind:after and @bind:get / @bind:set modifiers do, and how do they simplify handling logic after a value changes?
@bind:after and @bind:get / @bind:set modifiers do, and how do they simplify handling logic after a value changes?These modifiers give you control around a bound value without hand-writing the full Value/ValueChanged plumbing: @bind:after runs logic after the value updates, and @bind:get/@bind:set let you supply the read and write halves explicitly.
@bind:after:
Runs a callback (often async) after the bound field has been assigned, e.g. triggering a search once the text updates.
You no longer resort to manual event handlers just to react to a change.
@bind:get / @bind:set:
get supplies the current value; set receives the new value, letting you validate, transform, or call logic inside the setter.
They must be used together and replace the older two-way workaround of a property with a custom backing setter.
Why they matter: They make side-effect-on-change logic declarative and keep the two-way binding intact instead of forcing you to drop back to raw @onchange.
Q61.How is a Blazor app's root wired up: what are the roles of App.razor, the Routes/Router component, and MapRazorComponents?
App.razor, the Routes/Router component, and MapRazorComponents?The root wiring connects the ASP.NET Core host to the Blazor component tree: App.razor is the top-level component (the HTML document host), a Router component (often in Routes.razor) maps URLs to pages, and MapRazorComponents registers the root component as an endpoint in the server pipeline.
App.razor: In .NET 8+ it renders the full HTML document (<html>, <head>, <body>) and includes the <Routes /> component and script references.
Router / Routes component: The Router scans an assembly for @page routes; Found renders the matched page through RouteView with a DefaultLayout, and NotFound handles unmatched URLs.
MapRazorComponents: Called in Program.cs to register the root component (App) as an endpoint and enable render modes via .AddInteractiveServerRenderMode() / .AddInteractiveWebAssemblyRenderMode().
The flow: Request hits the mapped endpoint, App.razor renders the document, the Router resolves the URL to a page, and that page renders inside its layout.
Q62.How do you apply a format string when binding a value with @bind (for example a date)?
@bind (for example a date)?Use the @bind:format modifier to specify a .NET format string that controls how the value is displayed in and parsed from the input.
Syntax: Add @bind:format="..." alongside @bind, passing a standard or custom format string such as yyyy-MM-dd.
What it affects: The format is applied both when rendering the value into the input and when parsing the typed text back into the bound member.
Limitations:
It works with types that support format-string round-tripping (notably DateTime / DateTimeOffset); it is not a general formatter for arbitrary types.
For a native date picker use <input type="date">, which expects the yyyy-MM-dd format.
Q63.Explain the 'Interactive Auto' render mode introduced in .NET 8: how does it transition from server-side to client-side and what is the benefit for the end-user?
.NET 8: how does it transition from server-side to client-side and what is the benefit for the end-user?Interactive Auto is a per-component render mode that starts a component using Interactive Server (over SignalR) for a fast first load, then silently switches to Interactive WebAssembly on subsequent visits once the WebAssembly runtime and assemblies have been downloaded and cached.
First visit: runs on the server:
The component is interactive almost immediately via a SignalR circuit, avoiding the WASM download delay.
Meanwhile the .NET WebAssembly runtime and app DLLs download in the background and are cached by the browser.
Later visits: runs on the client: Once assets are cached, the component renders with Interactive WebAssembly, so no server circuit or network round-trips are needed for UI events.
Benefit to the end-user: Best of both: fast startup (server) with no long WASM wait, plus offline-capable, low-latency interactivity (client) afterward.
Caveat: code must run correctly in both environments: You can't rely on server-only resources (direct DB access, server file system); call shared services through an API so behavior is identical on server and client.
Q64.Explain the difference between Blazor Server and Blazor WebAssembly, focusing on where code executes, the role of SignalR vs the Mono/WASM runtime, and the trade-offs in latency and startup time.
Blazor Server and Blazor WebAssembly, focusing on where code executes, the role of SignalR vs the Mono/WASM runtime, and the trade-offs in latency and startup time.In Blazor Server your C# runs on the server and UI updates travel over a SignalR connection, while in Blazor WebAssembly your C# is downloaded and runs entirely in the browser on a WASM-based .NET runtime. The core trade-off is Server's tiny startup but per-interaction latency versus WASM's heavy first load but local, low-latency execution.
Where code executes:
Server: on the server; the browser only holds a thin JS client that applies DOM diffs.
WebAssembly: in the browser sandbox; the server (if any) is just an API.
Transport / runtime:
Server uses a persistent SignalR (WebSocket) circuit: events go up, render diffs come down.
WASM ships the Mono/.NET runtime compiled to WebAssembly plus your assemblies; there is no UI circuit.
Latency:
Server: every UI interaction is a network round-trip, so it suffers on slow/high-latency networks and needs a live connection.
WASM: interactions are handled locally, so UI is snappy and works offline.
Startup time & size:
Server starts fast (little to download).
WASM has a larger initial download (runtime + DLLs), slower first load.
Scalability & offline:
Server holds memory/circuit state per connected user, limiting scale; requires connectivity.
WASM offloads work to the client and can run as an offline PWA.
Q65.How does the .NET runtime actually run in a browser for Blazor WebAssembly?
.NET runtime actually run in a browser for Blazor WebAssembly?Blazor WebAssembly downloads a .NET runtime that has been compiled to WebAssembly and runs it inside the browser's standard WASM engine; that runtime then loads and executes your app's regular .NET assemblies (DLLs) client-side, with no plugins.
WebAssembly is the host: WASM is a portable, sandboxed bytecode that all modern browsers execute at near-native speed: it's the compilation target for the runtime.
The .NET runtime ships as WASM: A Mono-based .NET runtime compiled to WebAssembly is downloaded and started in the browser.
Your code stays as normal IL assemblies:
By default the runtime interprets your app's .NET DLLs; it does not recompile your C# to WASM.
AOT compilation is optional: it compiles your code to WebAssembly ahead of time for speed at the cost of a larger download.
DOM and browser access: WASM can't touch the DOM directly, so Blazor uses JS interop to update the page and call browser APIs.
Consequences: Everything runs client-side within the browser sandbox: no server round-trips for UI, but a larger initial download for runtime plus assemblies.
Q66.When would you choose Interactive Auto over just Interactive WebAssembly?
Interactive Auto over just Interactive WebAssembly?Choose Interactive Auto when you want the fast first load of Server plus the offline/scalable client execution of WebAssembly: it starts on the server via SignalR, then silently downloads the WASM runtime in the background and switches to the client on later visits.
Pick Auto over pure WebAssembly when first-load latency matters: Pure InteractiveWebAssembly blocks interactivity until the .NET runtime and DLLs download; Auto shows an interactive Server-rendered UI immediately.
Pick Auto when you still want to offload to the client eventually: After the WASM assets are cached, subsequent components run in the browser, freeing server resources and surviving connection drops.
Stick with pure WebAssembly when: You need a truly static host / offline PWA with no server dependency at all, or you can't run a persistent SignalR circuit.
Trade-offs to defend:
Auto requires your components to run correctly under both modes, so code must avoid server-only APIs and account for two execution environments.
You ship the Server hosting cost plus the WASM payload, so it's the most complex hosting model.
Q67.What are the scalability trade-offs of Blazor Server compared to a traditional MVC app?
MVC app?Blazor Server keeps a stateful SignalR circuit and per-user UI state in server memory for the whole session, so it scales by memory and connection count rather than by simple stateless request throughput like MVC.
MVC is stateless per request: Each request is independent, easy to load-balance and scale horizontally; the server holds nothing between requests.
Blazor Server is stateful per user: Every connected user consumes a live circuit with their component tree in RAM, so memory grows with concurrent users, not just request rate.
Persistent connection cost: Each UI event is a round trip over SignalR (WebSocket); latency directly affects responsiveness, and mobile/flaky networks drop the circuit.
Scale-out needs sticky sessions: A circuit is bound to one server, so you need affinity or a backplane, unlike stateless MVC that any node can serve.
Where Server wins: Small download, full .NET server APIs, and low per-request CPU for internal apps with modest user counts and good connectivity.
Q68.How does unhandled-exception behavior differ between Blazor Server and Blazor WebAssembly?
In Blazor Server an unhandled exception is considered fatal to the circuit: the server tears down the connection and the UI freezes; in WebAssembly it runs entirely in the browser, so an unhandled exception is logged to the browser console and can leave the app in an undefined state without killing a server connection.
Blazor Server:
An unhandled exception terminates the circuit for security (state could be corrupted); the user sees a dismissible error UI and must reload.
Exception details are kept on the server and not leaked to the client by default.
Blazor WebAssembly:
There's no circuit to drop; the exception surfaces in the browser console and the app may keep running in a broken state.
Details are inherently visible client-side, so avoid putting secrets in messages.
Common handling in both:
Wrap regions in an <ErrorBoundary> to catch and show fallback UI without killing the whole app.
Customize the blazor-error-ui element and log via ILogger.
Q69.How do you apply @rendermode at the component or page level, and can you mix multiple render modes on a single page?
@rendermode at the component or page level, and can you mix multiple render modes on a single page?You set render mode with the @rendermode directive on a component instance or at the top of a component/page, and yes, you can mix modes on one page as long as a component isn't nested under one with a different interactive mode.
Two ways to apply it:
On a child usage: <Counter @rendermode="InteractiveServer" />.
At the top of a component definition: @rendermode InteractiveWebAssembly (applies to that component wherever used).
Available modes: InteractiveServer, InteractiveWebAssembly, InteractiveAuto, and static SSR when none is set.
Mixing on one page is allowed:
A static-SSR page can host an island of interactive components, each with its own mode.
Restriction: you can't apply a different render mode to a component whose parent already has an interactive mode; children inherit the parent's mode.
Parameters passed to an interactive component must be serializable.
Q70.How do you handle component disposal for long-lived resources, and how do IDisposable and IAsyncDisposable work in the context of a Blazor circuit?
IDisposable and IAsyncDisposable work in the context of a Blazor circuit?Implement IDisposable or IAsyncDisposable on the component to release long-lived resources (timers, event subscriptions, JS object references, DB connections); Blazor calls Dispose when the component is removed or the circuit ends, preventing memory leaks in a long-lived Server circuit.
Declare it in markup: Use @implements IDisposable or @implements IAsyncDisposable and put cleanup in the method.
Synchronous IDisposable: For unsubscribing events, disposing Timer, cancelling a CancellationTokenSource: quick, non-blocking cleanup.
IAsyncDisposable: For cleanup that awaits, such as disposing an IJSObjectReference or async streams; use DisposeAsync.
Why it matters in a circuit:
Server circuits are long-lived, so undisposed subscriptions keep components alive and leak memory across the session.
Always unsubscribe from external events (e.g. a state service's OnChange) to avoid the component being rooted after it's gone.
Caveat: During teardown JS interop may already be unavailable, so guard interop calls in DisposeAsync and catch JSDisconnectedException.
Q71.What is the purpose of SetParametersAsync, and when would you override it instead of using the other lifecycle methods?
SetParametersAsync, and when would you override it instead of using the other lifecycle methods?SetParametersAsync is the very first lifecycle method called when parameters arrive from the parent: it assigns incoming values to your [Parameter] properties and then invokes the rest of the lifecycle. You override it only when you need to intercept or customize parameter assignment itself.
Its default job: Calls parameters.SetParameterProperties(this) to map the ParameterView onto properties, then runs OnInitialized/OnParametersSet and their async pairs.
When to override it:
Custom parameter handling: inspect or transform values before they are assigned, or skip expensive work when a value hasn't changed.
Manual parameter reading: read from the ParameterView directly (e.g. parameters.TryGetValue(...)) for advanced scenarios.
Validation or gating before the standard lifecycle runs.
The rule: if you override it, you must call base.SetParametersAsync(parameters) (or set the properties yourself), otherwise the rest of the lifecycle never fires.
Q72.Why is the @key directive critical when rendering lists of components or elements, and what is its role in the render tree diffing process?
@key directive critical when rendering lists of components or elements, and what is its role in the render tree diffing process?@key gives each item in a list a stable identity so the diffing algorithm can match elements/components across renders by key instead of by position, preserving state and avoiding incorrect reuse.
The problem without @key:
The diff matches old and new elements by their index in the sequence.
Inserting, removing, or reordering shifts positions, so Blazor may reuse the wrong element: component state, focus, or input values get attached to the wrong row.
What @key changes:
The diff matches by key value, so a moved item is moved (not rebuilt) and a removed item's component is disposed correctly.
Preserves per-item component instances and their internal state across reorders.
Choosing a key: Use a stable, unique value like an entity Id, not the loop index (which defeats the purpose).
Q73.What is the purpose of ShouldRender(), and how can it be used for performance optimization?
ShouldRender(), and how can it be used for performance optimization?ShouldRender is a lifecycle hook that lets a component veto a re-render: returning false skips building and diffing the render tree, which saves work when you know nothing visible changed.
How it works:
Called before every render except the first (the initial render always happens).
Return true to allow the render, false to suppress it.
Performance use:
Skip renders for components that receive frequent triggers but rarely change output (e.g. high-frequency parameter updates).
Guard by comparing old vs new state and returning false when equal, reducing diff work and (in Server) SignalR traffic.
Caution: Overriding it wrong causes stale UI that never updates: use it only when you can cheaply and reliably prove nothing changed.
Q74.How does Blazor's Render Tree and diffing algorithm minimize the amount of data sent over the SignalR circuit in Blazor Server?
SignalR circuit in Blazor Server?In Blazor Server the UI lives on the server; only the differences between the old and new render trees are serialized and pushed over the SignalR circuit as small edit instructions, not full HTML.
The render tree: An in-memory representation of the component's output the server keeps between renders.
Diffing:
On each render Blazor builds a new tree and compares it to the previous one, producing a minimal set of edits (add/remove/update attribute or text).
Only those edits are batched and sent to the browser, which patches the real DOM.
Why it saves bandwidth:
A one-character text change sends one small update, not the whole component markup.
@key helps produce move edits instead of full rebuilds for reordered lists.
ShouldRender and reducing render frequency further shrink the number of diffs generated.
Q75.When is it necessary to manually call StateHasChanged(), and what are the performance implications of calling it too frequently?
StateHasChanged(), and what are the performance implications of calling it too frequently?You must call StateHasChanged whenever state changes outside Blazor's automatic triggers, but calling it excessively wastes render and diffing cycles (and SignalR bandwidth in Server).
When it is necessary:
Updates from a System.Timers.Timer or background thread.
State changed by a non-UI event, external service callback, or message from a hub/subscription.
After partial updates during a long async operation where you want intermediate UI feedback.
From another thread, marshal it: await InvokeAsync(StateHasChanged).
Performance implications of calling too often:
Each call re-runs the render and diff for the component, costing CPU.
In Blazor Server, every resulting diff is serialized over the circuit, so high-frequency calls can flood the connection and cause UI lag.
Calling inside tight loops or per-item updates is a common mistake: batch the changes, then call it once.
Mitigations: coalesce updates, throttle high-frequency sources, and use ShouldRender to suppress no-op renders.
Q76.Explain how Blazor's diffing algorithm and the render tree work to update the UI.
Blazor builds an in-memory render tree (a lightweight description of the UI) each time a component renders, then diffs the new tree against the previous one and applies only the minimal set of changes to the real DOM.
The render tree:
Your .razor markup compiles to a BuildRenderTree method that emits a sequence of frames describing elements, attributes, text, and child components.
It is a C# representation of the UI, not the DOM itself, so it is cheap to build and compare.
The diff:
When a component re-renders, the new tree is compared to the previous snapshot to produce a set of edits (add/remove/update).
Only those edits are serialized and applied to the browser DOM, avoiding full re-rendering.
What triggers a render: Lifecycle events, UI events, and explicit StateHasChanged calls mark a component dirty and queue it for re-render.
Why keys matter: For lists, @key helps the diff match elements to the right identity so it moves rather than recreates them, preserving state and improving performance.
Q77.Why do you sometimes need to wrap StateHasChanged in InvokeAsync, and what is the renderer's synchronization context?
StateHasChanged in InvokeAsync, and what is the renderer's synchronization context?Q78.What is Attribute Splatting and CaptureUnmatchedValues, and why are they useful for wrapper components?
CaptureUnmatchedValues, and why are they useful for wrapper components?Q79.What are generic components in Blazor, and how does the @typeparam directive work?
@typeparam directive work?Q80.What are the common strategies for managing state in a large Blazor app, comparing component-local state, cascading values, and app-state services?
Q81.When should you use a CascadingParameter instead of a standard [Parameter], and what are the performance costs of deeply nested cascading values?
CascadingParameter instead of a standard [Parameter], and what are the performance costs of deeply nested cascading values?Q82.What are named cascading values, and why would you use them instead of a single unnamed CascadingValue?
CascadingValue?Q83.What is the role of the EditContext in Blazor forms and how does it track validation states and field changes?
EditContext in Blazor forms and how does it track validation states and field changes?Q84.How do you build a custom input component by inheriting from InputBase<T>?
InputBase<T>?Q85.What are JavaScript Initializers (beforeStart, afterStarted), and what are they used for?
beforeStart, afterStarted), and what are they used for?Q86.What is the purpose of the AuthenticationStateProvider, and how do you customize it to handle JWT-based auth in a WASM app?
AuthenticationStateProvider, and how do you customize it to handle JWT-based auth in a WASM app?Q87.How does Blazor handle authentication and authorization differently in Server vs. WASM?
Q88.What is 'Streaming Rendering' in Blazor, and how does it improve the perceived performance of Static Server-Side Rendering pages?
Q89.Explain 'Enhanced Navigation' and 'Enhanced Form Handling' in .NET 8 and how Blazor intercepts requests to make SSR feel like a SPA.
SSR feel like a SPA.Q90.How does Enhanced Navigation in .NET 8 differ from traditional SPA routing?
Q91.In .NET 8, how does 'Enhanced Form Handling' allow a Blazor SSR page to feel like a SPA without requiring full interactivity?
SSR page to feel like a SPA without requiring full interactivity?Q92.How does antiforgery protection work for Blazor SSR forms in .NET 8?
Q93.Explain the Double Render problem during prerendering: why does OnInitializedAsync run twice, and how do you prevent side effects or flickering?
OnInitializedAsync run twice, and how do you prevent side effects or flickering?Q94.What is the purpose of PersistentComponentState and how do you use it to pass state from server-side prerender to the client-side interactive component?
PersistentComponentState and how do you use it to pass state from server-side prerender to the client-side interactive component?Q95.What is Ahead-of-Time (AOT) compilation in Blazor WASM, and what are the trade-offs regarding file size and execution speed?
Q96.What is Assembly Trimming and Lazy Loading in Blazor WASM, and why are they critical for production apps?
Q97.How do you optimize the initial load time and payload size of a Blazor WASM application?
Q98.What is 'Intermediate Language (IL) Trimming' and why is it essential for Blazor WebAssembly deployment?
Q99.What is a CircuitHandler in Blazor Server, and what scenarios would you use it for?
CircuitHandler in Blazor Server, and what scenarios would you use it for?Q100.How do service lifetimes (Transient, Scoped, Singleton) behave differently in Blazor Server vs. Blazor WebAssembly, and why is 'Scoped' per-circuit in Server but Singleton-like in WASM?
Transient, Scoped, Singleton) behave differently in Blazor Server vs. Blazor WebAssembly, and why is 'Scoped' per-circuit in Server but Singleton-like in WASM?Q101.In Blazor Server, why is it dangerous to use a standard Scoped DbContext, and what is the recommended alternative?
DbContext, and what is the recommended alternative?Q102.What is the purpose of OwningComponentBase, and how does it help manage the lifetime of dependencies within a specific component?
OwningComponentBase, and how does it help manage the lifetime of dependencies within a specific component?