ASP.NET Core Razor components
This article explains how to create and use Razor components in Blazor apps, including guidance on Razor syntax, component naming, namespaces, and component parameters.
Blazor apps are built using Razor components, informally known as Blazor components. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and used in MVC and Razor Pages apps.
Component classes
Components are implemented using a combination of C# and HTML markup in Razor component files with the .razor
file extension.
By default, ComponentBase is the base class for components described by Razor component files. ComponentBase implements the lowest abstraction of components, the IComponent interface. ComponentBase defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events.
ComponentBase
in dotnet/aspnetcore
reference source: The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice.
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Developers typically create Razor components from Razor component files (.razor
) or base their components on ComponentBase, but components can also be built by implementing IComponent. Developer-built components that implement IComponent can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain.
Razor syntax
Components use Razor syntax. Two Razor features are extensively used by components, directives and directive attributes. These are reserved keywords prefixed with @
that appear in Razor markup:
- Directives: Change the way component markup is parsed or functions. For example, the
@page
directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. - Directive attributes: Change the way a component element is parsed or functions. For example, the
@bind
directive attribute for an<input>
element binds data to the element's value.
Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see Razor syntax reference for ASP.NET Core.
Names
A component's name must start with an uppercase character:
ProductDetail.razor
is valid.productDetail.razor
is invalid.
Common Blazor naming conventions used throughout the Blazor documentation include:
- Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example,
Pages/ProductDetail.razor
indicates that theProductDetail
component has a file name ofProductDetail.razor
and resides in thePages
folder of the app. - Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a
ProductDetail
component with a route template of/product-detail
(@page "/product-detail"
) is requested in a browser at the relative URL/product-detail
.
†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word.
Routing
Routing in Blazor is achieved by providing a route template to each accessible component in the app with an @page
directive. When a Razor file with an @page
directive is compiled, the generated class is given a RouteAttribute specifying the route template. At runtime, the router searches for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL.
The following HelloWorld
component uses a route template of /hello-world
, and the rendered webpage for the component is reached at the relative URL /hello-world
. Components that produce webpages usually reside in the Pages
folder, but you can use any folder to hold components, including within nested folders.
Pages/HelloWorld.razor
:
@page "/hello-world"
<h1>Hello World!</h1>
The preceding component loads in the browser at /hello-world
regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the NavMenu
component so that a link to the component appears in the app's UI-based navigation.
For the preceding HelloWorld
component, you can add a NavLink
component to the NavMenu
component in the Shared
folder. For more information, including descriptions of the NavLink
and NavMenu
components, see ASP.NET Core Blazor routing and navigation.
Markup
A component's UI is defined using Razor syntax, which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file.
Members of the component class are defined in one or more @code
blocks. In @code
blocks, component state is specified and processed with C#:
- Property and field initializers.
- Parameter values from arguments passed by parent components and route parameters.
- Methods for user event handling, lifecycle events, and custom component logic.
Component members are used in rendering logic using C# expressions that start with the @
symbol. For example, a C# field is rendered by prefixing @
to the field name. The following Markup
component evaluates and renders:
headingFontStyle
for the CSS property valuefont-style
of the heading element.headingText
for the content of the heading element.
Pages/Markup.razor
:
@page "/markup"
<h1 style="font-style:@headingFontStyle">@headingText</h1>
@code {
private string headingFontStyle = "italic";
private string headingText = "Put on your new Blazor!";
}
Note
Examples throughout the Blazor documentation specify the private
access modifier for private members. Private members are scoped to a component's class. However, C# assumes the private
access modifier when no access modifier is present, so explicitly marking members "private
" in your own code is optional. For more information on access modifiers, see Access Modifiers (C# Programming Guide).
The Blazor framework processes a component internally as a render tree, which is the combination of a component's Document Object Model (DOM) and Cascading Style Sheet Object Model (CSSOM). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see ASP.NET Core Razor component rendering.
Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: @if
, @code
, @bind
). Property names are uppercase (example: @Body
for LayoutComponentBase.Body).
Asynchronous methods (async
) don't support returning void
The Blazor framework doesn't track void
-returning asynchronous methods (async
). As a result, exceptions aren't caught if void
is returned. Always return a Task from asynchronous methods.
Nested components
Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type.
Consider the following Heading
component, which can be used by other components to display a heading.
Shared/Heading.razor
:
<h1 style="font-style:@headingFontStyle">Heading Example</h1>
@code {
private string headingFontStyle = "italic";
}
The following markup in the HeadingExample
component renders the preceding Heading
component at the location where the <Heading />
tag appears.
Pages/HeadingExample.razor
:
@page "/heading-example"
<Heading />
If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an @using
directive for the component's namespace makes the component available, which resolves the warning. For more information, see the Class name and namespace section.
The Heading
component example shown in this section doesn't have an @page
directive, so the Heading
component isn't directly accessible to a user via a direct request in the browser. However, any component with an @page
directive can be nested in another component. If the Heading
component was directly accessible by including @page "/heading"
at the top of its Razor file, then the component would be rendered for browser requests at both /heading
and /heading-example
.
Class name and namespace
Components are ordinary C# classes and can be placed anywhere within a project. Components that produce webpages usually reside in the Pages
folder. Non-page components are frequently placed in the Shared
folder or a custom folder added to the project.
Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is BlazorSample
and the Counter
component resides in the Pages
folder:
- The
Counter
component's namespace isBlazorSample.Pages
. - The fully qualified type name of the component is
BlazorSample.Pages.Counter
.
For custom folders that hold components, add an @using
directive to the parent component or to the app's _Imports.razor
file. The following example makes components in the Components
folder available:
@using BlazorSample.Components
Note
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
).
Components can also be referenced using their fully qualified names, which doesn't require an @using
directive. The following example directly references the ProductDetail
component in the Components
folder of the app:
<BlazorSample.Components.ProductDetail />
The namespace of a component authored with Razor is based on the following (in priority order):
- The
@namespace
directive in the Razor file's markup (for example,@namespace BlazorSample.CustomNamespace
). - The project's
RootNamespace
in the project file (for example,<RootNamespace>BlazorSample</RootNamespace>
). - The project name, taken from the project file's file name (
.csproj
), and the path from the project root to the component. For example, the framework resolves{PROJECT ROOT}/Pages/Index.razor
with a project namespace ofBlazorSample
(BlazorSample.csproj
) to the namespaceBlazorSample.Pages
for theIndex
component.{PROJECT ROOT}
is the project root path. Components follow C# name binding rules. For theIndex
component in this example, the components in scope are all of the components:- In the same folder,
Pages
. - The components in the project's root that don't explicitly specify a different namespace.
- In the same folder,
The following are not supported:
- The
global::
qualification. - Importing components with aliased
using
statements. For example,@using Foo = Bar
isn't supported. - Partially-qualified names. For example, you can't add
@using BlazorSample
to a component and then reference theNavMenu
component in the app'sShared
folder (Shared/NavMenu.razor
) with<Shared.NavMenu></Shared.NavMenu>
.
Partial class support
Components are generated as C# partial classes and are authored using either of the following approaches:
- A single file contains C# code defined in one or more
@code
blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. - HTML and Razor markup are placed in a Razor file (
.razor
). C# code is placed in a code-behind file defined as a partial class (.cs
).
Note
A component stylesheet that defines component-specific styles is a separate file (.css
). Blazor CSS isolation is described later in ASP.NET Core Blazor CSS isolation.
The following example shows the default Counter
component with an @code
block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring.
Pages/Counter.razor
:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The following Counter
component splits HTML and Razor markup from C# code using a code-behind file with a partial class:
Pages/CounterPartialClass.razor
:
@page "/counter-partial-class"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Pages/CounterPartialClass.razor.cs
:
namespace BlazorSample.Pages;
public partial class CounterPartialClass
{
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
). Add namespaces to a partial class file as needed.
Typical namespaces used by components:
using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;
Typical namespaces also include the namespace of the app and the namespace corresponding to the app's Shared
folder:
using BlazorSample;
using BlazorSample.Shared;
Specify a base class
The @inherits
directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The BlazorRocksBase
base class derives from ComponentBase.
Pages/BlazorRocks.razor
:
@page "/blazor-rocks"
@inherits BlazorRocksBase
<h1>@BlazorRocksText</h1>
BlazorRocksBase.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase : ComponentBase
{
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
}
Component parameters
Component parameters pass data to components and are defined using public C# properties on the component class with the [Parameter]
attribute. In the following example, a built-in reference type (System.String) and a user-defined reference type (PanelBody
) are passed as component parameters.
PanelBody.cs
:
public class PanelBody
{
public string? Text { get; set; }
public string? Style { get; set; }
}
Shared/ParameterChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">@Title</div>
<div class="card-body" style="font-style:@Body.Style">
@Body.Text
</div>
</div>
@code {
[Parameter]
public string Title { get; set; } = "Set By Child";
[Parameter]
public PanelBody Body { get; set; } =
new()
{
Text = "Set by child.",
Style = "normal"
};
}
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
The Title
and Body
component parameters of the ParameterChild
component are set by arguments in the HTML tag that renders the instance of the component. The following ParameterParent
component renders two ParameterChild
components:
- The first
ParameterChild
component is rendered without supplying parameter arguments. - The second
ParameterChild
component receives values forTitle
andBody
from theParameterParent
component, which uses an explicit C# expression to set the values of thePanelBody
's properties.
Pages/ParameterParent.razor
:
@page "/parameter-parent"
<h1>Child component (without attribute values)</h1>
<ParameterChild />
<h1>Child component (with attribute values)</h1>
<ParameterChild Title="Set by Parent"
Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />
The following rendered HTML markup from the ParameterParent
component shows ParameterChild
component default values when the ParameterParent
component doesn't supply component parameter values. When the ParameterParent
component provides component parameter values, they replace the ParameterChild
component's default values.
Note
For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup.
<h1>Child component (without attribute values)</h1>
<div>
<div>Set By Child</div>
<div>Set by child.</div>
</div>
<h1>Child component (with attribute values)</h1>
<div>
<div>Set by Parent</div>
<div>Set by parent.</div>
</div>
Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using Razor's reserved @
symbol. The following ParameterParent2
component displays four instances of the preceding ParameterChild
component and sets their Title
parameter values to:
- The value of the
title
field. - The result of the
GetTitle
C# method. - The current local date in long format with ToLongDateString, which uses an implicit C# expression.
- The
panelData
object'sTitle
property.
The @
prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set.
Outside of string parameters, we recommend the use of the @
prefix for nonliterals, even when they aren't strictly required.
We don't recommend the use of the @
prefix for literals (for example, boolean values), keywords (for example, this
), or null
, but you can choose to use them if you wish. For example, IsFixed="@true"
is uncommon but supported.
Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, Value=this
is supported, instead of Value="this"
. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies.
Throughout the documentation, code examples:
- Always use quotes. Example:
Value="this"
. - Nonliterals always use the
@
prefix, even when it's optional. Examples:Title="@title"
, wheretitle
is a string-typed variable.Count="@ct"
, wherect
is a number-typed variable. - Literals, outside of Razor expressions, always avoid
@
. Example:IsFixed="true"
.
Pages/ParameterParent2.razor
:
@page "/parameter-parent-2"
<ParameterChild Title="@title" />
<ParameterChild Title="@GetTitle()" />
<ParameterChild Title="@DateTime.Now.ToLongDateString()" />
<ParameterChild Title="@panelData.Title" />
@code {
private string title = "From Parent field";
private PanelData panelData = new();
private string GetTitle()
{
return "From Parent method";
}
private class PanelData
{
public string Title { get; set; } = "From Parent object";
}
}
Note
When assigning a C# member to a component parameter, prefix the member with the @
symbol and never prefix the parameter's HTML attribute.
Correct:
<ParameterChild Title="@title" />
Incorrect:
<ParameterChild @Title="title" />
Unlike in Razor pages (.cshtml
), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the asynchronous lifecycle events. After each asynchronous lifecycle event, the component may render again. The following Razor syntax is not supported:
<ParameterChild Title="@await ..." />
The code in the preceding example generates a compiler error when the app is built:
The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
To obtain a value for the Title
parameter in the preceding example asynchronously, the component can use the OnInitializedAsync
lifecycle event, as the following example demonstrates:
<ParameterChild Title="@title" />
@code {
private string? title;
protected override async Task OnInitializedAsync()
{
title = await ...;
}
}
For more information, see ASP.NET Core Razor component lifecycle.
Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is not supported. The following example seeks to concatenate the text "Set by
" with an object's property value. Although this syntax is supported in a Razor page (.cshtml
), it isn't valid for assignment to the child's Title
parameter in a component. The following Razor syntax is not supported:
<ParameterChild Title="Set by @(panelData.Title)" />
The code in the preceding example generates a compiler error when the app is built:
Component attributes do not support complex content (mixed C# and markup).
To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "Set by
" and an object's property value in the C# method GetTitle
:
Pages/ParameterParent3.razor
:
@page "/parameter-parent-3"
<ParameterChild Title="@GetTitle()" />
@code {
private PanelData panelData = new();
private string GetTitle() => $"Set by {panelData.Title}";
private class PanelData
{
public string Title { get; set; } = "Parent";
}
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
Component parameters should be declared as auto-properties, meaning that they shouldn't contain custom logic in their get
or set
accessors. For example, the following StartData
property is an auto-property:
[Parameter]
public DateTime StartData { get; set; }
Don't place custom logic in the get
or set
accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a set
accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results.
To transform a received parameter value:
- Leave the parameter property as an auto-property to represent the supplied raw data.
- Create a different property or method to supply the transformed data based on the parameter property.
Override OnParametersSetAsync
to transform a received parameter each time new data is received.
Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local DateTime with DateTime.Now to StartData
is valid syntax in a component:
[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;
After the initial assignment of DateTime.Now, do not assign a value to StartData
in developer code. For more information, see the Overwritten parameters section of this article.
Apply the [EditorRequired]
attribute to specify a required component parameter. If a parameter value isn't provided, editors or build tools may display warnings to the user. This attribute is only valid on properties also marked with the [Parameter]
attribute. The EditorRequiredAttribute is enforced at design-time and when the app is built. The attribute isn't enforced at runtime, and it doesn't guarantee a non-null
parameter value.
[Parameter]
[EditorRequired]
public string? Title { get; set; }
Single-line attribute lists are also supported:
[Parameter, EditorRequired]
public string? Title { get; set; }
Tuples
(API documentation) are supported for component parameters and RenderFragment
types. The following component parameter example passes three values in a Tuple
:
Shared/RenderTupleChild.razor
:
<div class="card w-50" style="margin-bottom:15px">
<div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
<div class="card-body">
<ul>
<li>Integer: @Data?.Item1</li>
<li>String: @Data?.Item2</li>
<li>Boolean: @Data?.Item3</li>
</ul>
</div>
</div>
@code {
[Parameter]
public Tuple<int, string, bool>? Data { get; set; }
}
Pages/RenderTupleParent.razor
:
@page "/render-tuple-parent"
<h1>Render <code>Tuple</code> Parent</h1>
<RenderTupleChild Data="@data" />
@code {
private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true);
}
Only unnamed tuples are supported for C# 7.0 or later in Razor components. Named tuples support in Razor components is planned for a future ASP.NET Core release. For more information, see Blazor Transpiler issue with named Tuples (dotnet/aspnetcore #28982).
Quote ©2005 Universal Pictures: Serenity (Nathan Fillion)
Route parameters
Components can specify route parameters in the route template of the @page
directive. The Blazor router uses route parameters to populate corresponding component parameters.
Optional route parameters are supported. In the following example, the text
optional parameter assigns the value of the route segment to the component's Text
property. If the segment isn't present, the value of Text
is set to "fantastic
" in the OnInitialized
lifecycle method.
Pages/RouteParameter.razor
:
@page "/route-parameter/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
For information on catch-all route parameters ({*pageRoute}
), which capture paths across multiple folder boundaries, see ASP.NET Core Blazor routing and navigation.
Child content render fragments
Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags.
In the following example, the RenderFragmentChild
component has a ChildContent
component parameter that represents a segment of the UI to render as a RenderFragment. The position of ChildContent
in the component's Razor markup is where the content is rendered in the final HTML output.
Shared/RenderFragmentChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">Child content</div>
<div class="card-body">@ChildContent</div>
</div>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
Important
The property receiving the RenderFragment content must be named ChildContent
by convention.
Event callbacks aren't supported for RenderFragment.
The following RenderFragmentParent
component provides content for rendering the RenderFragmentChild
by placing the content inside the child component's opening and closing tags.
Pages/RenderFragmentParent.razor
:
@page "/render-fragment-parent"
<h1>Render child content</h1>
<RenderFragmentChild>
Content of the child component is supplied
by the parent component.
</RenderFragmentChild>
Due to the way that Blazor renders child content, rendering components inside a for
loop requires a local index variable if the incrementing loop variable is used in the RenderFragmentChild
component's content. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Three children with an index variable</h1>
@for (int c = 0; c < 3; c++)
{
var current = c;
<RenderFragmentChild>
Count: @current
</RenderFragmentChild>
}
Alternatively, use a foreach
loop with Enumerable.Range instead of a for
loop. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Second example of three children with an index variable</h1>
@foreach (var c in Enumerable.Range(0,3))
{
<RenderFragmentChild>
Count: @c
</RenderFragmentChild>
}
Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections:
- Blazor layouts
- Pass data across a component hierarchy
- Templated components
- Global exception handling
Note
Blazor framework's built-in Razor components use the same ChildContent
component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name ChildContent
in the API documentation (filters API with the search term "ChildContent").
Render fragments for reusable rendering logic
You can factor out child components purely as a way of reusing rendering logic. In any component's @code
block, define a RenderFragment and render the fragment from any location as many times as needed:
<h1>Hello, world!</h1>
@RenderWelcomeInfo
<p>Render the welcome info a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = __builder =>
{
<p>Welcome to your new app!</p>
};
}
For more information, see Reuse rendering logic.
Overwritten parameters
The Blazor framework generally imposes safe parent-to-child parameter assignment:
- Parameters aren't overwritten unexpectedly.
- Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops.
A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:
- The child component is rendered with one or more parameter values from the parent component.
- The child writes directly to the value of a parameter.
- The parent component rerenders and overwrites the value of the child's parameter.
The potential for overwriting parameter values extends into the child component's property set
accessors, too.
Important
Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time.
Consider the following Expander
component that:
- Renders child content.
- Toggles showing child content with a component parameter (
Expanded
).
After the following Expander
component demonstrates an overwritten parameter, a modified Expander
component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>
@if (Expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
[Parameter]
public bool Expanded { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
private void Toggle()
{
Expanded = !Expanded;
}
}
The Expander
component is added to the following ExpanderExample
parent component that may call StateHasChanged:
- Calling StateHasChanged in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. StateHasChanged is covered in more detail later in ASP.NET Core Razor component lifecycle and ASP.NET Core Razor component rendering.
- The button's
@onclick
directive attribute attaches an event handler to the button'sonclick
event. Event handling is covered in more detail later in ASP.NET Core Blazor event handling.
Pages/ExpanderExample.razor
:
@page "/expander-example"
<Expander Expanded="true">
Expander 1 content
</Expander>
<Expander Expanded="true" />
<button @onclick="StateHasChanged">
Call StateHasChanged
</button>
Initially, the Expander
components behave independently when their Expanded
properties are toggled. The child components maintain their states as expected.
If StateHasChanged is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed:
- For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed.
- For unchecked parameter types, Blazor rerenders the child component regardless of whether or not the parameters have changed. Child content falls into this category of parameter types because child content is of type RenderFragment, which is a delegate that refers to other mutable objects.
For the ExpanderExample
component:
- The first
Expander
component sets child content in a potentially mutable RenderFragment, so a call to StateHasChanged in the parent component automatically rerenders the component and potentially overwrites the value ofExpanded
to its initial value oftrue
. - The second
Expander
component doesn't set child content. Therefore, a potentially mutable RenderFragment doesn't exist. A call to StateHasChanged in the parent component doesn't automatically rerender the child component, so the component'sExpanded
value isn't overwritten.
To maintain state in the preceding scenario, use a private field in the Expander
component to maintain its toggled state.
The following revised Expander
component:
- Accepts the
Expanded
component parameter value from the parent. - Assigns the component parameter value to a private field (
expanded
) in theOnInitialized
event. - Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter.
Note
The advice in this section extends to similar logic in component parameter set
accessors, which can result in similar undesirable side effects.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>
@if (expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
private bool expanded;
[Parameter]
public bool Expanded { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
protected override void OnInitialized()
{
expanded = Expanded;
}
private void Toggle()
{
expanded = !expanded;
}
}
For two-way parent-child binding examples, see ASP.NET Core Blazor data binding. For additional information, see Blazor Two Way Binding Error (dotnet/aspnetcore #24599).
For more information on change detection, including information on the exact types that Blazor checks, see ASP.NET Core Razor component rendering.
Attribute splatting and arbitrary parameters
Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes
Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an <input>
that supports many parameters.
In the following Splat
component:
- The first
<input>
element (id="useIndividualParams"
) uses individual component parameters. - The second
<input>
element (id="useAttributesDict"
) uses attribute splatting.
Pages/Splat.razor
:
@page "/splat"
<input id="useIndividualParams"
maxlength="@maxlength"
placeholder="@placeholder"
required="@required"
size="@size" />
<input id="useAttributesDict"
@attributes="InputAttributes" />
@code {
private string maxlength = "10";
private string placeholder = "Input placeholder text";
private string required = "required";
private string size = "50";
private Dictionary<string, object> InputAttributes { get; set; } =
new()
{
{ "maxlength", "10" },
{ "placeholder", "Input placeholder text" },
{ "required", "required" },
{ "size", "50" }
};
}
The rendered <input>
elements in the webpage are identical:
<input id="useIndividualParams"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
<input id="useAttributesDict"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
To accept arbitrary attributes, define a component parameter with the CaptureUnmatchedValues property set to true
:
@code {
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? InputAttributes { get; set; }
}
The CaptureUnmatchedValues property on [Parameter]
allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with CaptureUnmatchedValues. The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object>
with string keys. Use of IEnumerable<KeyValuePair<string, object>>
or IReadOnlyDictionary<string, object>
are also options in this scenario.
The position of @attributes
relative to the position of element attributes is important. When @attributes
are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component:
Shared/AttributeOrderChild1.razor
:
<div @attributes="AdditionalAttributes" extra="5" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent1.razor
:
@page "/attribute-order-parent-1"
<AttributeOrderChild1 extra="10" />
The AttributeOrderChild1
component's extra
attribute is set to the right of @attributes
. The AttributeOrderParent1
component's rendered <div>
contains extra="5"
when passed through the additional attribute because the attributes are processed right to left (last to first):
<div extra="5" />
In the following example, the order of extra
and @attributes
is reversed in the child component's <div>
:
Shared/AttributeOrderChild2.razor
:
<div extra="5" @attributes="AdditionalAttributes" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent2.razor
:
@page "/attribute-order-parent-2"
<AttributeOrderChild2 extra="10" />
The <div>
in the parent component's rendered webpage contains extra="10"
when passed through the additional attribute:
<div extra="10" />
Capture references to components
Component references provide a way to reference a component instance for issuing commands. To capture a component reference:
- Add an
@ref
attribute to the child component. - Define a field with the same type as the child component.
When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance.
Consider the following ReferenceChild
component that logs a message when its ChildMethod
is called.
Shared/ReferenceChild.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger
@code {
public void ChildMethod(int value)
{
logger.LogInformation("Received {Value} in ChildMethod", value);
}
}
A component reference is only populated after the component is rendered and its output includes ReferenceChild
's element. Until the component is rendered, there's nothing to reference.
To manipulate component references after the component has finished rendering, use the OnAfterRender
or OnAfterRenderAsync
methods.
To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the OnAfterRender
or OnAfterRenderAsync
methods. This ensures that the reference variable is assigned before the event handler is assigned.
The following lambda approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent1.razor
:
@page "/reference-parent-1"
<button @onclick="@(() => childComponent?.ChildMethod(5))">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild? childComponent;
}
The following delegate approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent2.razor
:
@page "/reference-parent-2"
<button @onclick="@(() => callChildMethod?.Invoke())">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild? childComponent;
private Action? callChildMethod;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
callChildMethod = CallChildMethod;
}
}
private void CallChildMethod()
{
childComponent?.ChildMethod(5);
}
}
While capturing component references use a similar syntax to capturing element references, capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code.
Important
Do not use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the component parameters section and the ASP.NET Core Blazor data binding article.
Synchronization context
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and event callbacks raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently.
Avoid thread-blocking calls
Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying Task is complete:
Note
Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling Thread.Sleep.
Invoke component methods externally to update state
In the event a component must be updated based on an external event, such as a timer or other notification, use the InvokeAsync
method, which dispatches code execution to Blazor's synchronization context. For example, consider the following notifier service that can notify any listening component about updated state. The Update
method can be called from anywhere in the app.
TimerService.cs
:
public class TimerService : IDisposable
{
private int elapsedCount;
private readonly static TimeSpan heartbeatTickRate = TimeSpan.FromSeconds(5);
private readonly ILogger<TimerService> logger;
private readonly NotifierService notifier;
private PeriodicTimer? timer;
public TimerService(NotifierService notifier,
ILogger<TimerService> logger)
{
this.notifier = notifier;
this.logger = logger;
}
public async Task Start()
{
if (timer is null)
{
timer = new(heartbeatTickRate);
logger.LogInformation("Started");
using (timer)
{
while (await timer.WaitForNextTickAsync())
{
elapsedCount += 1;
await notifier.Update("elapsedCount", elapsedCount);
logger.LogInformation($"elapsedCount: {elapsedCount}");
}
}
}
}
public void Dispose()
{
timer?.Dispose();
}
}
NotifierService.cs
:
public class NotifierService
{
public async Task Update(string key, int value)
{
if (Notify != null)
{
await Notify.Invoke(key, value);
}
}
public event Func<string, int, Task>? Notify;
}
Register the services:
In a Blazor WebAssembly app, register the services as singletons in
Program.cs
:builder.Services.AddSingleton<NotifierService>(); builder.Services.AddSingleton<TimerService>();
In a Blazor Server app, register the services as scoped in
Program.cs
:builder.Services.AddScoped<NotifierService>(); builder.Services.AddScoped<TimerService>();
Use the NotifierService
to update a component.
Pages/ReceiveNotifications.razor
:
@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer
<h1>Receive Notifications</h1>
<h2>Timer Service</h2>
<button @onclick="StartTimer">Start Timer</button>
<h2>Notifications</h2>
<p>
Status:
@if (lastNotification.key is not null)
{
<span>@lastNotification.key = @lastNotification.value</span>
}
else
{
<span>Awaiting first notification</span>
}
</p>
@code {
private (string key, int value) lastNotification;
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public async Task OnNotify(string key, int value)
{
await InvokeAsync(() =>
{
lastNotification = (key, value);
StateHasChanged();
});
}
private async Task StartTimer()
{
await Timer.Start();
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
In the preceding example:
NotifierService
invokes the component'sOnNotify
method outside of Blazor's synchronization context.InvokeAsync
is used to switch to the correct context and queue a render. For more information, see ASP.NET Core Razor component rendering.- The component implements IDisposable. The
OnNotify
delegate is unsubscribed in theDispose
method, which is called by the framework when the component is disposed. For more information, see ASP.NET Core Razor component lifecycle.
Important
If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (ExecutionContext) at the time the handler is registered. For more information, see Calling InvokeAsync(StateHasChanged)
causes page to fallback to default culture (dotnet/aspnetcore #28521).
Use @key
to control the preservation of elements and components
When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the @key
directive attribute is required.
Consider the following example that demonstrates a collection mapping problem that's solved by using @key
.
For the following Details
and PeopleExample
components:
- The
Details
component receives data (Data
) from the parentPeopleExample
component, which is displayed in an<input>
element. Any given displayed<input>
element can receive the focus of the page from the user when they select one of the<input>
elements. - The
PeopleExample
component creates a list of person objects for display using theDetails
component. Every three seconds, a new person is added to the collection.
This demonstration allows you to:
- Select an
<input>
from among several renderedDetails
components. - Study the behavior of the page's focus as the people collection automatically grows.
Shared/Details.razor
:
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
In the following PeopleExample
component, each iteration of adding a person in OnTimerCallback
results in Blazor rebuilding the entire collection. The page's focus remains on the same index position of <input>
elements, so the focus shifts each time a person is added. Shifting the focus away from what the user selected isn't desirable behavior. After demonstrating the poor behavior with the following component, the @key
directive attribute is used to improve the user's experience.
Pages/PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
The contents of the people
collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the people
collection, the user's focus is lost.
The mapping process of elements or components to a collection can be controlled with the @key
directive attribute. Use of @key
guarantees the preservation of elements or components based on the key's value. If the Details
component in the preceding example is keyed on the person
item, Blazor ignores rerendering Details
components that haven't changed.
To modify the PeopleExample
component to use the @key
directive attribute with the people
collection, update the <Details>
element to the following:
<Details @key="person" Data="@person.Data" />
When the people
collection changes, the association between Details
instances and person
instances is retained. When a Person
is inserted at the beginning of the collection, one new Details
instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection.
Other collection updates exhibit the same behavior when the @key
directive attribute is used:
- If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged.
- If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI.
Important
Keys are local to each container element or component. Keys aren't compared globally across the document.
When to use @key
Typically, it makes sense to use @key
whenever a list is rendered (for example, in a foreach
block) and a suitable value exists to define the @key
.
You can also use @key
to preserve an element or component subtree when an object doesn't change, as the following examples show.
Example 1:
<li @key="person">
<input value="@person.Data" />
</li>
Example 2:
<div @key="person">
@* other HTML elements *@
</div>
If an person
instance changes, the @key
attribute directive forces Blazor to:
- Discard the entire
<li>
or<div>
and their descendants. - Rebuild the subtree within the UI with new elements and components.
This is useful to guarantee that no UI state is preserved when the collection changes within a subtree.
Scope of @key
The @key
attribute directive is scoped to its own siblings within its parent.
Consider the following example. The first
and second
keys are compared against each other within the same scope of the outer <div>
element:
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
The following example demonstrates first
and second
keys in their own scopes, unrelated to each other and without influence on each other. Each @key
scope only applies to its parent <div>
element, not across the parent <div>
elements:
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
For the Details
component shown earlier, the following examples render person
data within the same @key
scope and demonstrate typical use cases for @key
:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
The following examples only scope @key
to the <div>
or <li>
element that surrounds each Details
component instance. Therefore, person
data for each member of the people
collection is not keyed on each person
instance across the rendered Details
components. Avoid the following patterns when using @key
:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
When not to use @key
There's a performance cost when rendering with @key
. The performance cost isn't large, but only specify @key
if preserving the element or component benefits the app.
Even if @key
isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using @key
is control over how model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping.
Values to use for @key
Generally, it makes sense to supply one of the following values for @key
:
- Model object instances. For example, the
Person
instance (person
) was used in the earlier example. This ensures preservation based on object reference equality. - Unique identifiers. For example, unique identifiers can be based on primary key values of type
int
,string
, orGuid
.
Ensure that values used for @key
don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values.
Apply an attribute
Attributes can be applied to components with the @attribute
directive. The following example applies the [Authorize]
attribute to the component's class:
@page "/"
@attribute [Authorize]
Conditional HTML element attributes
HTML element attribute properties are conditionally set based on the .NET value. If the value is false
or null
, the property isn't set. If the value is true
, the property is set.
In the following example, IsCompleted
determines if the <input>
element's checked
property is set.
Pages/ConditionalAttribute.razor
:
@page "/conditional-attribute"
<label>
<input type="checkbox" checked="@IsCompleted" />
Is Completed?
</label>
<button @onclick="@(() => IsCompleted = !IsCompleted)">
Change IsCompleted
</button>
@code {
[Parameter]
public bool IsCompleted { get; set; }
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Some HTML attributes, such as aria-pressed
, don't function properly when the .NET type is a bool
. In those cases, use a string
type instead of a bool
.
Raw HTML
Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a MarkupString value. The value is parsed as HTML or SVG and inserted into the DOM.
Warning
Rendering raw HTML constructed from any untrusted source is a security risk and should always be avoided.
The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component.
Pages/MarkupStringExample.razor
:
@page "/markup-string-example"
@((MarkupString)myMarkup)
@code {
private string myMarkup =
"<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}
Razor templates
Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format:
@<{HTML tag}>...</{HTML tag}>
The following example illustrates how to specify RenderFragment and RenderFragment<TValue> values and render templates directly in a component. Render fragments can also be passed as arguments to templated components.
Pages/RazorTemplate.razor
:
@page "/razor-template"
@timeTemplate
@petTemplate(new Pet { Name = "Nutty Rex" })
@code {
private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;
private class Pet
{
public string? Name { get; set; }
}
}
Rendered output of the preceding code:
<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>
Static assets
Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's web root
(wwwroot
) folder or folders under the wwwroot
folder.
Use a base-relative path (/
) to refer to the web root for a static asset. In the following example, logo.png
is physically located in the {PROJECT ROOT}/wwwroot/images
folder. {PROJECT ROOT}
is the app's project root.
<img alt="Company logo" src="/images/logo.png" />
Components do not support tilde-slash notation (~/
).
For information on setting an app's base path, see Host and deploy ASP.NET Core Blazor.
Tag Helpers aren't supported in components
Tag Helpers
aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead.
Scalable Vector Graphics (SVG) images
Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg
), are supported via the <img>
tag:
<img alt="Example image" src="image.svg" />
Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css
):
.element-class {
background-image: url("image.svg");
}
Blazor supports the <foreignObject>
element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a RenderFragment, or a Razor component.
The following example demonstrates:
- Display of a
string
(@message
). - Two-way binding with an
<input>
element and avalue
field. - A
Robot
component.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black"
fill="none" />
<foreignObject x="20" y="20" width="160" height="160">
<p>@message</p>
</foreignObject>
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="200" height="200">
<label>
Two-way binding:
<input @bind="value" @bind:event="oninput" />
</label>
</foreignObject>
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject>
<Robot />
</foreignObject>
</svg>
@code {
private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
private string? value;
}
Whitespace rendering behavior
Unless the @preservewhitespace
directive is used with a value of true
, extra whitespace is removed by default if:
- Leading or trailing within an element.
- Leading or trailing within a RenderFragment/RenderFragment<TValue> parameter (for example, child content passed to another component).
- It precedes or follows a C# code block, such as
@if
or@foreach
.
Whitespace removal might affect the rendered output when using a CSS rule, such as white-space: pre
. To disable this performance optimization and preserve the whitespace, take one of the following actions:
- Add the
@preservewhitespace true
directive at the top of the Razor file (.razor
) to apply the preference to a specific component. - Add the
@preservewhitespace true
directive inside an_Imports.razor
file to apply the preference to a subdirectory or to the entire project.
In most cases, no action is required, as apps typically continue to behave normally (but faster). If stripping whitespace causes a rendering problem for a particular component, use @preservewhitespace true
in that component to disable this optimization.
Generic type parameter support
The @typeparam
directive declares a generic type parameter for the generated component class:
@typeparam TItem
C# syntax with where
type constraints is supported:
@typeparam TEntity where TEntity : IEntity
In the following example, the ListGenericTypeItems1
component is generically typed as TExample
.
Shared/ListGenericTypeItems1.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul>
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList{ get; set; }
}
The following GenericTypeExample1
component renders two ListGenericTypeItems1
components:
- String or integer data is assigned to the
ExampleList
parameter of each component. - Type
string
orint
that matches the type of the assigned data is set for the type parameter (TExample
) of each component.
Pages/GenericTypeExample1.razor
:
@page "/generic-type-example-1"
<h1>Generic Type Example 1</h1>
<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })"
TExample="string" />
<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })"
TExample="int" />
For more information, see Razor syntax reference for ASP.NET Core. For an example of generic typing with templated components, see ASP.NET Core Blazor templated components.
Cascaded generic type support
An ancestor component can cascade a type parameter by name to descendants using the [CascadingTypeParameter]
attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
By adding @attribute [CascadingTypeParameter(...)]
to a component, the specified generic type argument is automatically used by descendants that:
- Are nested as child content for the component in the same
.razor
document. - Also declare a
@typeparam
with the exact same name. - Don't have another value explicitly supplied or implicitly inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type.
When receiving a cascaded type parameter, components obtain the parameter value from the closest ancestor that has a CascadingTypeParameterAttribute with a matching name. Cascaded generic type parameters are overridden within a particular subtree.
Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example T
or TItem
. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
Generic types can be cascaded to child components in either of the following approaches with ancestor (parent) components, which are demonstrated in the following two sub-sections:
- Explicitly set the cascaded generic type.
- Infer the cascaded generic type.
The following subsections provide examples of the preceding approaches using the following two ListDisplay
components. The components receive and render list data and are generically typed as TExample
. These components are for demonstration purposes and only differ in the color of text that the list is rendered. If you wish to experiment with the components in the following sub-sections in a local test app, add the following two components to the app first.
Shared/ListDisplay1.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:blue">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListDisplay2.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:red">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
}
Explicit generic types based on ancestor components
The demonstration in this section cascades a type explicitly for TExample
.
Note
This section uses the two ListDisplay
components in the Cascaded generic type support section.
The following ListGenericTypeItems2
component receives data and cascades a generic type parameter named TExample
to its descendent components. In the upcoming parent component, the ListGenericTypeItems2
component is used to display list data with the preceding ListDisplay
component.
Shared/ListGenericTypeItems2.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 2</h2>
@ChildContent
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
The following GenericTypeExample2
parent component sets the child content (RenderFragment) of two ListGenericTypeItems2
components specifying the ListGenericTypeItems2
types (TExample
), which are cascaded to child components. ListDisplay
components are rendered with the list item data shown in the example. String data is used with the first ListGenericTypeItems2
component, and integer data is used with the second ListGenericTypeItems2
component.
Pages/GenericTypeExample2.razor
:
@page "/generic-type-example-2"
<h1>Generic Type Example 2</h1>
<ListGenericTypeItems2 TExample="string">
<ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
<ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems2>
<ListGenericTypeItems2 TExample="int">
<ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
<ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems2>
Specifying the type explicitly also allows the use of cascading values and parameters to provide data to child components, as the following demonstration shows.
Shared/ListDisplay3.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:blue">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListDisplay4.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:red">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListGenericTypeItems3.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 3</h2>
@ChildContent
@if (ExampleList is not null)
{
<ul style="color:green">
@foreach(var item in ExampleList)
{
<li>@item</li>
}
</ul>
<p>
Type of <code>TExample</code>: @typeof(TExample)
</p>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
When cascading the data in the following example, the type must be provided to the ListGenericTypeItems3
component.
Pages/GenericTypeExample3.razor
:
@page "/generic-type-example-3"
<h1>Generic Type Example 3</h1>
<CascadingValue Value="@stringData">
<ListGenericTypeItems3 TExample="string">
<ListDisplay3 />
<ListDisplay4 />
</ListGenericTypeItems3>
</CascadingValue>
<CascadingValue Value="@integerData">
<ListGenericTypeItems3 TExample="int">
<ListDisplay3 />
<ListDisplay4 />
</ListGenericTypeItems3>
</CascadingValue>
@code {
private List<string> stringData = new() { "Item 1", "Item 2" };
private List<int> integerData = new() { 1, 2, 3 };
}
When multiple generic types are cascaded, values for all generic types in the set must be passed. In the following example, TItem
, TValue
, and TEdit
are GridColumn
generic types, but the parent component that places GridColumn
doesn't specify the TItem
type:
<GridColumn TValue="string" TEdit="@TextEdit" />
The preceding example generates a compile-time error that the GridColumn
component is missing the TItem
type parameter. Valid code specifies all of the types:
<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" />
Infer generic types based on ancestor components
The demonstration in this section cascades a type inferred for TExample
.
Note
This section uses the two ListDisplay
components in the Cascaded generic type support section.
Shared/ListGenericTypeItems4.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 4</h2>
@ChildContent
@if (ExampleList is not null)
{
<ul style="color:green">
@foreach(var item in ExampleList)
{
<li>@item</li>
}
</ul>
<p>
Type of <code>TExample</code>: @typeof(TExample)
</p>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
The following GenericTypeExample4
component with inferred cascaded types provides different data for display.
Pages/GenericTypeExample4.razor
:
@page "/generic-type-example-4"
<h1>Generic Type Example 4</h1>
<ListGenericTypeItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
<ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
<ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems4>
<ListGenericTypeItems4 ExampleList="@(new List<int> { 7, 8, 9 })">
<ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
<ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems4>
The following GenericTypeExample5
component with inferred cascaded types provides the same data for display. The following example directly assigns the data to the components.
Pages/GenericTypeExample5.razor
:
@page "/generic-type-example-5"
<h1>Generic Type Example 5</h1>
<ListGenericTypeItems4 ExampleList="@stringData">
<ListDisplay1 ExampleList="@stringData" />
<ListDisplay2 ExampleList="@stringData" />
</ListGenericTypeItems4>
<ListGenericTypeItems4 ExampleList="@integerData">
<ListDisplay1 ExampleList="@integerData" />
<ListDisplay2 ExampleList="@integerData" />
</ListGenericTypeItems4>
@code {
private List<string> stringData = new() { "Item 1", "Item 2" };
private List<int> integerData = new() { 1, 2, 3 };
}
Render static root Razor components
A root Razor component is the first component loaded of any component hierarchy created by the app.
In an app created from the Blazor Server project template, the App
component (App.razor
) is created as the default root component in Pages/_Host.cshtml
using the Component Tag Helper:
<component type="typeof(App)" render-mode="ServerPrerendered" />
In an app created from the Blazor WebAssembly project template, the App
component (App.razor
) is created as the default root component in Program.cs
:
builder.RootComponents.Add<App>("#app");
In the preceding code, the CSS selector, #app
, indicates that the App
component is created for the <div>
in wwwroot/index.html
with an id
of app
:
<div id="app">...</app>
MVC and Razor Pages apps can also use the Component Tag Helper to register statically-rendered Blazor WebAssembly root components:
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
Statically-rendered components can only be added to the app. They can't be removed or updated afterwards.
For more information, see the following resources:
QuickGrid
component
The QuickGrid
component is a Razor component for quickly and efficiently displaying data in tabular form. QuickGrid
provides a simple and convenient data grid component for common grid rendering scenarios and serves as a reference architecture and performance baseline for building data grid components. QuickGrid
is highly optimized and uses advanced techniques to achieve optimal rendering performance.
To get started with QuickGrid
:
The QuickGrid
component is an experimental Razor component for quickly and efficiently displaying data in tabular form. QuickGrid
provides a simple and convenient data grid component for common grid rendering scenarios and serves as a reference architecture and performance baseline for building data grid components. QuickGrid
is highly optimized and uses advanced techniques to achieve optimal rendering performance.
To get started with QuickGrid
:
Add package reference for
Microsoft.AspNetCore.Components.QuickGrid
. If using the .NET CLI to add the package reference, include the--prerelease
option when you execute thedotnet add package
command.Note
For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.
Add the following component to render a grid.
Pages/QuickGridExample.razor
:@page "/quickgrid-example" @using Microsoft.AspNetCore.Components.QuickGrid <QuickGrid Items="@people"> <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" /> <PropertyColumn Property="@(p => p.Name)" Sortable="true" /> <PropertyColumn Property="@(p => p.BirthDate)" Format="yyyy-MM-dd" Sortable="true" /> </QuickGrid> @code { private record Person(int PersonId, string Name, DateOnly BirthDate); private IQueryable<Person> people = new[] { new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)), new Person(10944, "António Langa", new DateOnly(1991, 12, 1)), new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)), new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)), new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)), new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)), }.AsQueryable(); }
Access the component in a browser at the relative path
/quickgrid-example
.
For various QuickGrid
demonstrations, see the QuickGrid for Blazor app. The demo site is built using Blazor WebAssembly and is hosted on GitHub Pages. The site loads fast thanks to static prerendering using the community-maintained BlazorWasmPrerendering.Build
GitHub project.
There aren't current plans to extend QuickGrid
with features that full-blown commercial grids tend to offer, for example, hierarchical rows, drag-to-reorder columns, or Excel-like range selections. If you require advanced features that you don't wish to develop on your own, continue using third-party grids.
Warning
The QuickGrid
component is in preview. You're welcome to use it in production if it meets your needs, but it isn't officially supported and may change in future releases.
Blazor apps are built using Razor components, informally known as Blazor components. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and used in MVC and Razor Pages apps.
Component classes
Components are implemented using a combination of C# and HTML markup in Razor component files with the .razor
file extension.
By default, ComponentBase is the base class for components described by Razor component files. ComponentBase implements the lowest abstraction of components, the IComponent interface. ComponentBase defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events.
ComponentBase
in dotnet/aspnetcore
reference source: The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice.
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Developers typically create Razor components from Razor component files (.razor
) or base their components on ComponentBase, but components can also be built by implementing IComponent. Developer-built components that implement IComponent can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain.
Razor syntax
Components use Razor syntax. Two Razor features are extensively used by components, directives and directive attributes. These are reserved keywords prefixed with @
that appear in Razor markup:
- Directives: Change the way component markup is parsed or functions. For example, the
@page
directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. - Directive attributes: Change the way a component element is parsed or functions. For example, the
@bind
directive attribute for an<input>
element binds data to the element's value.
Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see Razor syntax reference for ASP.NET Core.
Names
A component's name must start with an uppercase character:
ProductDetail.razor
is valid.productDetail.razor
is invalid.
Common Blazor naming conventions used throughout the Blazor documentation include:
- Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example,
Pages/ProductDetail.razor
indicates that theProductDetail
component has a file name ofProductDetail.razor
and resides in thePages
folder of the app. - Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a
ProductDetail
component with a route template of/product-detail
(@page "/product-detail"
) is requested in a browser at the relative URL/product-detail
.
†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word.
Routing
Routing in Blazor is achieved by providing a route template to each accessible component in the app with an @page
directive. When a Razor file with an @page
directive is compiled, the generated class is given a RouteAttribute specifying the route template. At runtime, the router searches for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL.
The following HelloWorld
component uses a route template of /hello-world
. The rendered webpage for the component is reached at the relative URL /hello-world
. When running a Blazor app locally with the default protocol, host, and port, the HelloWorld
component is requested in the browser at https://localhost:5001/hello-world
. Components that produce webpages usually reside in the Pages
folder, but you can use any folder to hold components, including within nested folders.
Pages/HelloWorld.razor
:
@page "/hello-world"
<h1>Hello World!</h1>
The preceding component loads in the browser at /hello-world
regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the NavMenu
component so that a link to the component appears in the app's UI-based navigation.
For the preceding HelloWorld
component, you can add a NavLink
component to the NavMenu
component in the Shared
folder. For more information, including descriptions of the NavLink
and NavMenu
components, see ASP.NET Core Blazor routing and navigation.
Markup
A component's UI is defined using Razor syntax, which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file.
Members of the component class are defined in one or more @code
blocks. In @code
blocks, component state is specified and processed with C#:
- Property and field initializers.
- Parameter values from arguments passed by parent components and route parameters.
- Methods for user event handling, lifecycle events, and custom component logic.
Component members are used in rendering logic using C# expressions that start with the @
symbol. For example, a C# field is rendered by prefixing @
to the field name. The following Markup
component evaluates and renders:
headingFontStyle
for the CSS property valuefont-style
of the heading element.headingText
for the content of the heading element.
Pages/Markup.razor
:
@page "/markup"
<h1 style="font-style:@headingFontStyle">@headingText</h1>
@code {
private string headingFontStyle = "italic";
private string headingText = "Put on your new Blazor!";
}
Note
Examples throughout the Blazor documentation specify the private
access modifier for private members. Private members are scoped to a component's class. However, C# assumes the private
access modifier when no access modifier is present, so explicitly marking members "private
" in your own code is optional. For more information on access modifiers, see Access Modifiers (C# Programming Guide).
The Blazor framework processes a component internally as a render tree, which is the combination of a component's Document Object Model (DOM) and Cascading Style Sheet Object Model (CSSOM). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see ASP.NET Core Razor component rendering.
Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: @if
, @code
, @bind
). Property names are uppercase (example: @Body
for LayoutComponentBase.Body).
Asynchronous methods (async
) don't support returning void
The Blazor framework doesn't track void
-returning asynchronous methods (async
). As a result, exceptions aren't caught if void
is returned. Always return a Task from asynchronous methods.
Nested components
Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type.
Consider the following Heading
component, which can be used by other components to display a heading.
Shared/Heading.razor
:
<h1 style="font-style:@headingFontStyle">Heading Example</h1>
@code {
private string headingFontStyle = "italic";
}
The following markup in the HeadingExample
component renders the preceding Heading
component at the location where the <Heading />
tag appears.
Pages/HeadingExample.razor
:
@page "/heading-example"
<Heading />
If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an @using
directive for the component's namespace makes the component available, which resolves the warning. For more information, see the Class name and namespace section.
The Heading
component example shown in this section doesn't have an @page
directive, so the Heading
component isn't directly accessible to a user via a direct request in the browser. However, any component with an @page
directive can be nested in another component. If the Heading
component was directly accessible by including @page "/heading"
at the top of its Razor file, then the component would be rendered for browser requests at both /heading
and /heading-example
.
Class name and namespace
Components are ordinary C# classes and can be placed anywhere within a project. Components that produce webpages usually reside in the Pages
folder. Non-page components are frequently placed in the Shared
folder or a custom folder added to the project.
Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is BlazorSample
and the Counter
component resides in the Pages
folder:
- The
Counter
component's namespace isBlazorSample.Pages
. - The fully qualified type name of the component is
BlazorSample.Pages.Counter
.
For custom folders that hold components, add an @using
directive to the parent component or to the app's _Imports.razor
file. The following example makes components in the Components
folder available:
@using BlazorSample.Components
Note
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
).
Components can also be referenced using their fully qualified names, which doesn't require an @using
directive. The following example directly references the ProductDetail
component in the Components
folder of the app:
<BlazorSample.Components.ProductDetail />
The namespace of a component authored with Razor is based on the following (in priority order):
- The
@namespace
directive in the Razor file's markup (for example,@namespace BlazorSample.CustomNamespace
). - The project's
RootNamespace
in the project file (for example,<RootNamespace>BlazorSample</RootNamespace>
). - The project name, taken from the project file's file name (
.csproj
), and the path from the project root to the component. For example, the framework resolves{PROJECT ROOT}/Pages/Index.razor
with a project namespace ofBlazorSample
(BlazorSample.csproj
) to the namespaceBlazorSample.Pages
for theIndex
component.{PROJECT ROOT}
is the project root path. Components follow C# name binding rules. For theIndex
component in this example, the components in scope are all of the components:- In the same folder,
Pages
. - The components in the project's root that don't explicitly specify a different namespace.
- In the same folder,
The following are not supported:
- The
global::
qualification. - Importing components with aliased
using
statements. For example,@using Foo = Bar
isn't supported. - Partially-qualified names. For example, you can't add
@using BlazorSample
to a component and then reference theNavMenu
component in the app'sShared
folder (Shared/NavMenu.razor
) with<Shared.NavMenu></Shared.NavMenu>
.
Partial class support
Components are generated as C# partial classes and are authored using either of the following approaches:
- A single file contains C# code defined in one or more
@code
blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. - HTML and Razor markup are placed in a Razor file (
.razor
). C# code is placed in a code-behind file defined as a partial class (.cs
).
Note
A component stylesheet that defines component-specific styles is a separate file (.css
). Blazor CSS isolation is described later in ASP.NET Core Blazor CSS isolation.
The following example shows the default Counter
component with an @code
block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring.
Pages/Counter.razor
:
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The following Counter
component splits HTML and Razor markup from C# code using a code-behind file with a partial class:
Pages/CounterPartialClass.razor
:
@page "/counter-partial-class"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Pages/CounterPartialClass.razor.cs
:
namespace BlazorSample.Pages;
public partial class CounterPartialClass
{
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
). Add namespaces to a partial class file as needed.
Typical namespaces used by components:
using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;
Typical namespaces also include the namespace of the app and the namespace corresponding to the app's Shared
folder:
using BlazorSample;
using BlazorSample.Shared;
Specify a base class
The @inherits
directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The BlazorRocksBase
base class derives from ComponentBase.
Pages/BlazorRocks.razor
:
@page "/blazor-rocks"
@inherits BlazorRocksBase
<h1>@BlazorRocksText</h1>
BlazorRocksBase.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample;
public class BlazorRocksBase : ComponentBase
{
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
}
Component parameters
Component parameters pass data to components and are defined using public C# properties on the component class with the [Parameter]
attribute. In the following example, a built-in reference type (System.String) and a user-defined reference type (PanelBody
) are passed as component parameters.
PanelBody.cs
:
public class PanelBody
{
public string? Text { get; set; }
public string? Style { get; set; }
}
Shared/ParameterChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">@Title</div>
<div class="card-body" style="font-style:@Body.Style">
@Body.Text
</div>
</div>
@code {
[Parameter]
public string Title { get; set; } = "Set By Child";
[Parameter]
public PanelBody Body { get; set; } =
new()
{
Text = "Set by child.",
Style = "normal"
};
}
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
The Title
and Body
component parameters of the ParameterChild
component are set by arguments in the HTML tag that renders the instance of the component. The following ParameterParent
component renders two ParameterChild
components:
- The first
ParameterChild
component is rendered without supplying parameter arguments. - The second
ParameterChild
component receives values forTitle
andBody
from theParameterParent
component, which uses an explicit C# expression to set the values of thePanelBody
's properties.
Pages/ParameterParent.razor
:
@page "/parameter-parent"
<h1>Child component (without attribute values)</h1>
<ParameterChild />
<h1>Child component (with attribute values)</h1>
<ParameterChild Title="Set by Parent"
Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />
The following rendered HTML markup from the ParameterParent
component shows ParameterChild
component default values when the ParameterParent
component doesn't supply component parameter values. When the ParameterParent
component provides component parameter values, they replace the ParameterChild
component's default values.
Note
For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup.
<h1>Child component (without attribute values)</h1>
<div>
<div>Set By Child</div>
<div>Set by child.</div>
</div>
<h1>Child component (with attribute values)</h1>
<div>
<div>Set by Parent</div>
<div>Set by parent.</div>
</div>
Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using Razor's reserved @
symbol. The following ParameterParent2
component displays four instances of the preceding ParameterChild
component and sets their Title
parameter values to:
- The value of the
title
field. - The result of the
GetTitle
C# method. - The current local date in long format with ToLongDateString, which uses an implicit C# expression.
- The
panelData
object'sTitle
property.
The @
prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set.
Outside of string parameters, we recommend the use of the @
prefix for nonliterals, even when they aren't strictly required.
We don't recommend the use of the @
prefix for literals (for example, boolean values), keywords (for example, this
), or null
, but you can choose to use them if you wish. For example, IsFixed="@true"
is uncommon but supported.
Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, Value=this
is supported, instead of Value="this"
. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies.
Throughout the documentation, code examples:
- Always use quotes. Example:
Value="this"
. - Nonliterals always use the
@
prefix, even when it's optional. Examples:Title="@title"
, wheretitle
is a string-typed variable.Count="@ct"
, wherect
is a number-typed variable. - Literals, outside of Razor expressions, always avoid
@
. Example:IsFixed="true"
.
Pages/ParameterParent2.razor
:
@page "/parameter-parent-2"
<ParameterChild Title="@title" />
<ParameterChild Title="@GetTitle()" />
<ParameterChild Title="@DateTime.Now.ToLongDateString()" />
<ParameterChild Title="@panelData.Title" />
@code {
private string title = "From Parent field";
private PanelData panelData = new();
private string GetTitle()
{
return "From Parent method";
}
private class PanelData
{
public string Title { get; set; } = "From Parent object";
}
}
Note
When assigning a C# member to a component parameter, prefix the member with the @
symbol and never prefix the parameter's HTML attribute.
Correct:
<ParameterChild Title="@title" />
Incorrect:
<ParameterChild @Title="title" />
Unlike in Razor pages (.cshtml
), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the asynchronous lifecycle events. After each asynchronous lifecycle event, the component may render again. The following Razor syntax is not supported:
<ParameterChild Title="@await ..." />
The code in the preceding example generates a compiler error when the app is built:
The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
To obtain a value for the Title
parameter in the preceding example asynchronously, the component can use the OnInitializedAsync
lifecycle event, as the following example demonstrates:
<ParameterChild Title="@title" />
@code {
private string? title;
protected override async Task OnInitializedAsync()
{
title = await ...;
}
}
For more information, see ASP.NET Core Razor component lifecycle.
Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is not supported. The following example seeks to concatenate the text "Set by
" with an object's property value. Although this syntax is supported in a Razor page (.cshtml
), it isn't valid for assignment to the child's Title
parameter in a component. The following Razor syntax is not supported:
<ParameterChild Title="Set by @(panelData.Title)" />
The code in the preceding example generates a compiler error when the app is built:
Component attributes do not support complex content (mixed C# and markup).
To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "Set by
" and an object's property value in the C# method GetTitle
:
Pages/ParameterParent3.razor
:
@page "/parameter-parent-3"
<ParameterChild Title="@GetTitle()" />
@code {
private PanelData panelData = new();
private string GetTitle() => $"Set by {panelData.Title}";
private class PanelData
{
public string Title { get; set; } = "Parent";
}
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
Component parameters should be declared as auto-properties, meaning that they shouldn't contain custom logic in their get
or set
accessors. For example, the following StartData
property is an auto-property:
[Parameter]
public DateTime StartData { get; set; }
Don't place custom logic in the get
or set
accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a set
accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results.
To transform a received parameter value:
- Leave the parameter property as an auto-property to represent the supplied raw data.
- Create a different property or method to supply the transformed data based on the parameter property.
Override OnParametersSetAsync
to transform a received parameter each time new data is received.
Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local DateTime with DateTime.Now to StartData
is valid syntax in a component:
[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;
After the initial assignment of DateTime.Now, do not assign a value to StartData
in developer code. For more information, see the Overwritten parameters section of this article.
Apply the [EditorRequired]
attribute to specify a required component parameter. If a parameter value isn't provided, editors or build tools may display warnings to the user. This attribute is only valid on properties also marked with the [Parameter]
attribute. The EditorRequiredAttribute is enforced at design-time and when the app is built. The attribute isn't enforced at runtime, and it doesn't guarantee a non-null
parameter value.
[Parameter]
[EditorRequired]
public string? Title { get; set; }
Single-line attribute lists are also supported:
[Parameter, EditorRequired]
public string? Title { get; set; }
Tuples
(API documentation) are supported for component parameters and RenderFragment
types. The following component parameter example passes three values in a Tuple
:
Shared/RenderTupleChild.razor
:
<div class="card w-50" style="margin-bottom:15px">
<div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
<div class="card-body">
<ul>
<li>Integer: @Data?.Item1</li>
<li>String: @Data?.Item2</li>
<li>Boolean: @Data?.Item3</li>
</ul>
</div>
</div>
@code {
[Parameter]
public Tuple<int, string, bool>? Data { get; set; }
}
Pages/RenderTupleParent.razor
:
@page "/render-tuple-parent"
<h1>Render <code>Tuple</code> Parent</h1>
<RenderTupleChild Data="@data" />
@code {
private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true);
}
Only unnamed tuples are supported for C# 7.0 or later in Razor components. Named tuples support in Razor components is planned for a future ASP.NET Core release. For more information, see Blazor Transpiler issue with named Tuples (dotnet/aspnetcore #28982).
Quote ©2005 Universal Pictures: Serenity (Nathan Fillion)
Route parameters
Components can specify route parameters in the route template of the @page
directive. The Blazor router uses route parameters to populate corresponding component parameters.
Optional route parameters are supported. In the following example, the text
optional parameter assigns the value of the route segment to the component's Text
property. If the segment isn't present, the value of Text
is set to "fantastic
" in the OnInitialized
lifecycle method.
Pages/RouteParameter.razor
:
@page "/route-parameter/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
For information on catch-all route parameters ({*pageRoute}
), which capture paths across multiple folder boundaries, see ASP.NET Core Blazor routing and navigation.
Child content render fragments
Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags.
In the following example, the RenderFragmentChild
component has a ChildContent
component parameter that represents a segment of the UI to render as a RenderFragment. The position of ChildContent
in the component's Razor markup is where the content is rendered in the final HTML output.
Shared/RenderFragmentChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">Child content</div>
<div class="card-body">@ChildContent</div>
</div>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
Important
The property receiving the RenderFragment content must be named ChildContent
by convention.
Event callbacks aren't supported for RenderFragment.
The following RenderFragmentParent
component provides content for rendering the RenderFragmentChild
by placing the content inside the child component's opening and closing tags.
Pages/RenderFragmentParent.razor
:
@page "/render-fragment-parent"
<h1>Render child content</h1>
<RenderFragmentChild>
Content of the child component is supplied
by the parent component.
</RenderFragmentChild>
Due to the way that Blazor renders child content, rendering components inside a for
loop requires a local index variable if the incrementing loop variable is used in the RenderFragmentChild
component's content. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Three children with an index variable</h1>
@for (int c = 0; c < 3; c++)
{
var current = c;
<RenderFragmentChild>
Count: @current
</RenderFragmentChild>
}
Alternatively, use a foreach
loop with Enumerable.Range instead of a for
loop. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Second example of three children with an index variable</h1>
@foreach (var c in Enumerable.Range(0,3))
{
<RenderFragmentChild>
Count: @c
</RenderFragmentChild>
}
Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections:
- Blazor layouts
- Pass data across a component hierarchy
- Templated components
- Global exception handling
Note
Blazor framework's built-in Razor components use the same ChildContent
component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name ChildContent
in the API documentation (filters API with the search term "ChildContent").
Render fragments for reusable rendering logic
You can factor out child components purely as a way of reusing rendering logic. In any component's @code
block, define a RenderFragment and render the fragment from any location as many times as needed:
<h1>Hello, world!</h1>
@RenderWelcomeInfo
<p>Render the welcome info a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = __builder =>
{
<p>Welcome to your new app!</p>
};
}
For more information, see Reuse rendering logic.
Overwritten parameters
The Blazor framework generally imposes safe parent-to-child parameter assignment:
- Parameters aren't overwritten unexpectedly.
- Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops.
A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:
- The child component is rendered with one or more parameter values from the parent component.
- The child writes directly to the value of a parameter.
- The parent component rerenders and overwrites the value of the child's parameter.
The potential for overwriting parameter values extends into the child component's property set
accessors, too.
Important
Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time.
Consider the following Expander
component that:
- Renders child content.
- Toggles showing child content with a component parameter (
Expanded
).
After the following Expander
component demonstrates an overwritten parameter, a modified Expander
component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>
@if (Expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
[Parameter]
public bool Expanded { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
private void Toggle()
{
Expanded = !Expanded;
}
}
The Expander
component is added to the following ExpanderExample
parent component that may call StateHasChanged:
- Calling StateHasChanged in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. StateHasChanged is covered in more detail later in ASP.NET Core Razor component lifecycle and ASP.NET Core Razor component rendering.
- The button's
@onclick
directive attribute attaches an event handler to the button'sonclick
event. Event handling is covered in more detail later in ASP.NET Core Blazor event handling.
Pages/ExpanderExample.razor
:
@page "/expander-example"
<Expander Expanded="true">
Expander 1 content
</Expander>
<Expander Expanded="true" />
<button @onclick="StateHasChanged">
Call StateHasChanged
</button>
Initially, the Expander
components behave independently when their Expanded
properties are toggled. The child components maintain their states as expected.
If StateHasChanged is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed:
- For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed.
- For unchecked parameter types, Blazor rerenders the child component regardless of whether or not the parameters have changed. Child content falls into this category of parameter types because child content is of type RenderFragment, which is a delegate that refers to other mutable objects.
For the ExpanderExample
component:
- The first
Expander
component sets child content in a potentially mutable RenderFragment, so a call to StateHasChanged in the parent component automatically rerenders the component and potentially overwrites the value ofExpanded
to its initial value oftrue
. - The second
Expander
component doesn't set child content. Therefore, a potentially mutable RenderFragment doesn't exist. A call to StateHasChanged in the parent component doesn't automatically rerender the child component, so the component'sExpanded
value isn't overwritten.
To maintain state in the preceding scenario, use a private field in the Expander
component to maintain its toggled state.
The following revised Expander
component:
- Accepts the
Expanded
component parameter value from the parent. - Assigns the component parameter value to a private field (
expanded
) in theOnInitialized
event. - Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter.
Note
The advice in this section extends to similar logic in component parameter set
accessors, which can result in similar undesirable side effects.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>
@if (expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
private bool expanded;
[Parameter]
public bool Expanded { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
protected override void OnInitialized()
{
expanded = Expanded;
}
private void Toggle()
{
expanded = !expanded;
}
}
For two-way parent-child binding examples, see ASP.NET Core Blazor data binding. For additional information, see Blazor Two Way Binding Error (dotnet/aspnetcore #24599).
For more information on change detection, including information on the exact types that Blazor checks, see ASP.NET Core Razor component rendering.
Attribute splatting and arbitrary parameters
Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes
Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an <input>
that supports many parameters.
In the following Splat
component:
- The first
<input>
element (id="useIndividualParams"
) uses individual component parameters. - The second
<input>
element (id="useAttributesDict"
) uses attribute splatting.
Pages/Splat.razor
:
@page "/splat"
<input id="useIndividualParams"
maxlength="@maxlength"
placeholder="@placeholder"
required="@required"
size="@size" />
<input id="useAttributesDict"
@attributes="InputAttributes" />
@code {
private string maxlength = "10";
private string placeholder = "Input placeholder text";
private string required = "required";
private string size = "50";
private Dictionary<string, object> InputAttributes { get; set; } =
new()
{
{ "maxlength", "10" },
{ "placeholder", "Input placeholder text" },
{ "required", "required" },
{ "size", "50" }
};
}
The rendered <input>
elements in the webpage are identical:
<input id="useIndividualParams"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
<input id="useAttributesDict"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
To accept arbitrary attributes, define a component parameter with the CaptureUnmatchedValues property set to true
:
@code {
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? InputAttributes { get; set; }
}
The CaptureUnmatchedValues property on [Parameter]
allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with CaptureUnmatchedValues. The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object>
with string keys. Use of IEnumerable<KeyValuePair<string, object>>
or IReadOnlyDictionary<string, object>
are also options in this scenario.
The position of @attributes
relative to the position of element attributes is important. When @attributes
are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component:
Shared/AttributeOrderChild1.razor
:
<div @attributes="AdditionalAttributes" extra="5" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent1.razor
:
@page "/attribute-order-parent-1"
<AttributeOrderChild1 extra="10" />
The AttributeOrderChild1
component's extra
attribute is set to the right of @attributes
. The AttributeOrderParent1
component's rendered <div>
contains extra="5"
when passed through the additional attribute because the attributes are processed right to left (last to first):
<div extra="5" />
In the following example, the order of extra
and @attributes
is reversed in the child component's <div>
:
Shared/AttributeOrderChild2.razor
:
<div extra="5" @attributes="AdditionalAttributes" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent2.razor
:
@page "/attribute-order-parent-2"
<AttributeOrderChild2 extra="10" />
The <div>
in the parent component's rendered webpage contains extra="10"
when passed through the additional attribute:
<div extra="10" />
Capture references to components
Component references provide a way to reference a component instance for issuing commands. To capture a component reference:
- Add an
@ref
attribute to the child component. - Define a field with the same type as the child component.
When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance.
Consider the following ReferenceChild
component that logs a message when its ChildMethod
is called.
Shared/ReferenceChild.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger
@code {
public void ChildMethod(int value)
{
logger.LogInformation("Received {Value} in ChildMethod", value);
}
}
A component reference is only populated after the component is rendered and its output includes ReferenceChild
's element. Until the component is rendered, there's nothing to reference.
To manipulate component references after the component has finished rendering, use the OnAfterRender
or OnAfterRenderAsync
methods.
To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the OnAfterRender
or OnAfterRenderAsync
methods. This ensures that the reference variable is assigned before the event handler is assigned.
The following lambda approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent1.razor
:
@page "/reference-parent-1"
<button @onclick="@(() => childComponent?.ChildMethod(5))">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild? childComponent;
}
The following delegate approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent2.razor
:
@page "/reference-parent-2"
<button @onclick="@(() => callChildMethod?.Invoke())">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild? childComponent;
private Action? callChildMethod;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
callChildMethod = CallChildMethod;
}
}
private void CallChildMethod()
{
childComponent?.ChildMethod(5);
}
}
While capturing component references use a similar syntax to capturing element references, capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code.
Important
Do not use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the component parameters section and the ASP.NET Core Blazor data binding article.
Synchronization context
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and event callbacks raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently.
Avoid thread-blocking calls
Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying Task is complete:
Note
Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling Thread.Sleep.
Invoke component methods externally to update state
In the event a component must be updated based on an external event, such as a timer or other notification, use the InvokeAsync
method, which dispatches code execution to Blazor's synchronization context. For example, consider the following notifier service that can notify any listening component about updated state. The Update
method can be called from anywhere in the app.
TimerService.cs
:
public class TimerService : IDisposable
{
private int elapsedCount;
private readonly static TimeSpan heartbeatTickRate = TimeSpan.FromSeconds(5);
private readonly ILogger<TimerService> logger;
private readonly NotifierService notifier;
private PeriodicTimer? timer;
public TimerService(NotifierService notifier,
ILogger<TimerService> logger)
{
this.notifier = notifier;
this.logger = logger;
}
public async Task Start()
{
if (timer is null)
{
timer = new(heartbeatTickRate);
logger.LogInformation("Started");
using (timer)
{
while (await timer.WaitForNextTickAsync())
{
elapsedCount += 1;
await notifier.Update("elapsedCount", elapsedCount);
logger.LogInformation($"elapsedCount: {elapsedCount}");
}
}
}
}
public void Dispose()
{
timer?.Dispose();
}
}
NotifierService.cs
:
public class NotifierService
{
public async Task Update(string key, int value)
{
if (Notify != null)
{
await Notify.Invoke(key, value);
}
}
public event Func<string, int, Task>? Notify;
}
Register the services:
In a Blazor WebAssembly app, register the services as singletons in
Program.cs
:builder.Services.AddSingleton<NotifierService>(); builder.Services.AddSingleton<TimerService>();
In a Blazor Server app, register the services as scoped in
Program.cs
:builder.Services.AddScoped<NotifierService>(); builder.Services.AddScoped<TimerService>();
Use the NotifierService
to update a component.
Pages/ReceiveNotifications.razor
:
@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer
<h1>Receive Notifications</h1>
<h2>Timer Service</h2>
<button @onclick="StartTimer">Start Timer</button>
<h2>Notifications</h2>
<p>
Status:
@if (lastNotification.key is not null)
{
<span>@lastNotification.key = @lastNotification.value</span>
}
else
{
<span>Awaiting first notification</span>
}
</p>
@code {
private (string key, int value) lastNotification;
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public async Task OnNotify(string key, int value)
{
await InvokeAsync(() =>
{
lastNotification = (key, value);
StateHasChanged();
});
}
private async Task StartTimer()
{
await Timer.Start();
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
In the preceding example:
NotifierService
invokes the component'sOnNotify
method outside of Blazor's synchronization context.InvokeAsync
is used to switch to the correct context and queue a render. For more information, see ASP.NET Core Razor component rendering.- The component implements IDisposable. The
OnNotify
delegate is unsubscribed in theDispose
method, which is called by the framework when the component is disposed. For more information, see ASP.NET Core Razor component lifecycle.
Important
If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (ExecutionContext) at the time the handler is registered. For more information, see Calling InvokeAsync(StateHasChanged)
causes page to fallback to default culture (dotnet/aspnetcore #28521).
Use @key
to control the preservation of elements and components
When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the @key
directive attribute is required.
Consider the following example that demonstrates a collection mapping problem that's solved by using @key
.
For the following Details
and PeopleExample
components:
- The
Details
component receives data (Data
) from the parentPeopleExample
component, which is displayed in an<input>
element. Any given displayed<input>
element can receive the focus of the page from the user when they select one of the<input>
elements. - The
PeopleExample
component creates a list of person objects for display using theDetails
component. Every three seconds, a new person is added to the collection.
This demonstration allows you to:
- Select an
<input>
from among several renderedDetails
components. - Study the behavior of the page's focus as the people collection automatically grows.
Shared/Details.razor
:
<input value="@Data" />
@code {
[Parameter]
public string? Data { get; set; }
}
In the following PeopleExample
component, each iteration of adding a person in OnTimerCallback
results in Blazor rebuilding the entire collection. The page's focus remains on the same index position of <input>
elements, so the focus shifts each time a person is added. Shifting the focus away from what the user selected isn't desirable behavior. After demonstrating the poor behavior with the following component, the @key
directive attribute is used to improve the user's experience.
Pages/PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string? Data { get; set; }
}
}
The contents of the people
collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the people
collection, the user's focus is lost.
The mapping process of elements or components to a collection can be controlled with the @key
directive attribute. Use of @key
guarantees the preservation of elements or components based on the key's value. If the Details
component in the preceding example is keyed on the person
item, Blazor ignores rerendering Details
components that haven't changed.
To modify the PeopleExample
component to use the @key
directive attribute with the people
collection, update the <Details>
element to the following:
<Details @key="person" Data="@person.Data" />
When the people
collection changes, the association between Details
instances and person
instances is retained. When a Person
is inserted at the beginning of the collection, one new Details
instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection.
Other collection updates exhibit the same behavior when the @key
directive attribute is used:
- If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged.
- If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI.
Important
Keys are local to each container element or component. Keys aren't compared globally across the document.
When to use @key
Typically, it makes sense to use @key
whenever a list is rendered (for example, in a foreach
block) and a suitable value exists to define the @key
.
You can also use @key
to preserve an element or component subtree when an object doesn't change, as the following examples show.
Example 1:
<li @key="person">
<input value="@person.Data" />
</li>
Example 2:
<div @key="person">
@* other HTML elements *@
</div>
If an person
instance changes, the @key
attribute directive forces Blazor to:
- Discard the entire
<li>
or<div>
and their descendants. - Rebuild the subtree within the UI with new elements and components.
This is useful to guarantee that no UI state is preserved when the collection changes within a subtree.
Scope of @key
The @key
attribute directive is scoped to its own siblings within its parent.
Consider the following example. The first
and second
keys are compared against each other within the same scope of the outer <div>
element:
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
The following example demonstrates first
and second
keys in their own scopes, unrelated to each other and without influence on each other. Each @key
scope only applies to its parent <div>
element, not across the parent <div>
elements:
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
For the Details
component shown earlier, the following examples render person
data within the same @key
scope and demonstrate typical use cases for @key
:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
The following examples only scope @key
to the <div>
or <li>
element that surrounds each Details
component instance. Therefore, person
data for each member of the people
collection is not keyed on each person
instance across the rendered Details
components. Avoid the following patterns when using @key
:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
When not to use @key
There's a performance cost when rendering with @key
. The performance cost isn't large, but only specify @key
if preserving the element or component benefits the app.
Even if @key
isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using @key
is control over how model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping.
Values to use for @key
Generally, it makes sense to supply one of the following values for @key
:
- Model object instances. For example, the
Person
instance (person
) was used in the earlier example. This ensures preservation based on object reference equality. - Unique identifiers. For example, unique identifiers can be based on primary key values of type
int
,string
, orGuid
.
Ensure that values used for @key
don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values.
Apply an attribute
Attributes can be applied to components with the @attribute
directive. The following example applies the [Authorize]
attribute to the component's class:
@page "/"
@attribute [Authorize]
Conditional HTML element attributes
HTML element attribute properties are conditionally set based on the .NET value. If the value is false
or null
, the property isn't set. If the value is true
, the property is set.
In the following example, IsCompleted
determines if the <input>
element's checked
property is set.
Pages/ConditionalAttribute.razor
:
@page "/conditional-attribute"
<label>
<input type="checkbox" checked="@IsCompleted" />
Is Completed?
</label>
<button @onclick="@(() => IsCompleted = !IsCompleted)">
Change IsCompleted
</button>
@code {
[Parameter]
public bool IsCompleted { get; set; }
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Some HTML attributes, such as aria-pressed
, don't function properly when the .NET type is a bool
. In those cases, use a string
type instead of a bool
.
Raw HTML
Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a MarkupString value. The value is parsed as HTML or SVG and inserted into the DOM.
Warning
Rendering raw HTML constructed from any untrusted source is a security risk and should always be avoided.
The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component.
Pages/MarkupStringExample.razor
:
@page "/markup-string-example"
@((MarkupString)myMarkup)
@code {
private string myMarkup =
"<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}
Razor templates
Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format:
@<{HTML tag}>...</{HTML tag}>
The following example illustrates how to specify RenderFragment and RenderFragment<TValue> values and render templates directly in a component. Render fragments can also be passed as arguments to templated components.
Pages/RazorTemplate.razor
:
@page "/razor-template"
@timeTemplate
@petTemplate(new Pet { Name = "Nutty Rex" })
@code {
private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;
private class Pet
{
public string? Name { get; set; }
}
}
Rendered output of the preceding code:
<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>
Static assets
Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's web root
(wwwroot
) folder or folders under the wwwroot
folder.
Use a base-relative path (/
) to refer to the web root for a static asset. In the following example, logo.png
is physically located in the {PROJECT ROOT}/wwwroot/images
folder. {PROJECT ROOT}
is the app's project root.
<img alt="Company logo" src="/images/logo.png" />
Components do not support tilde-slash notation (~/
).
For information on setting an app's base path, see Host and deploy ASP.NET Core Blazor.
Tag Helpers aren't supported in components
Tag Helpers
aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead.
Scalable Vector Graphics (SVG) images
Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg
), are supported via the <img>
tag:
<img alt="Example image" src="image.svg" />
Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css
):
.element-class {
background-image: url("image.svg");
}
Blazor supports the <foreignObject>
element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a RenderFragment, or a Razor component.
The following example demonstrates:
- Display of a
string
(@message
). - Two-way binding with an
<input>
element and avalue
field. - A
Robot
component.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black"
fill="none" />
<foreignObject x="20" y="20" width="160" height="160">
<p>@message</p>
</foreignObject>
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="200" height="200">
<label>
Two-way binding:
<input @bind="value" @bind:event="oninput" />
</label>
</foreignObject>
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject>
<Robot />
</foreignObject>
</svg>
@code {
private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
private string? value;
}
Whitespace rendering behavior
Unless the @preservewhitespace
directive is used with a value of true
, extra whitespace is removed by default if:
- Leading or trailing within an element.
- Leading or trailing within a RenderFragment/RenderFragment<TValue> parameter (for example, child content passed to another component).
- It precedes or follows a C# code block, such as
@if
or@foreach
.
Whitespace removal might affect the rendered output when using a CSS rule, such as white-space: pre
. To disable this performance optimization and preserve the whitespace, take one of the following actions:
- Add the
@preservewhitespace true
directive at the top of the Razor file (.razor
) to apply the preference to a specific component. - Add the
@preservewhitespace true
directive inside an_Imports.razor
file to apply the preference to a subdirectory or to the entire project.
In most cases, no action is required, as apps typically continue to behave normally (but faster). If stripping whitespace causes a rendering problem for a particular component, use @preservewhitespace true
in that component to disable this optimization.
Generic type parameter support
The @typeparam
directive declares a generic type parameter for the generated component class:
@typeparam TItem
C# syntax with where
type constraints is supported:
@typeparam TEntity where TEntity : IEntity
In the following example, the ListGenericTypeItems1
component is generically typed as TExample
.
Shared/ListGenericTypeItems1.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul>
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList{ get; set; }
}
The following GenericTypeExample1
component renders two ListGenericTypeItems1
components:
- String or integer data is assigned to the
ExampleList
parameter of each component. - Type
string
orint
that matches the type of the assigned data is set for the type parameter (TExample
) of each component.
Pages/GenericTypeExample1.razor
:
@page "/generic-type-example-1"
<h1>Generic Type Example 1</h1>
<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })"
TExample="string" />
<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })"
TExample="int" />
For more information, see Razor syntax reference for ASP.NET Core. For an example of generic typing with templated components, see ASP.NET Core Blazor templated components.
Cascaded generic type support
An ancestor component can cascade a type parameter by name to descendants using the [CascadingTypeParameter]
attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
By adding @attribute [CascadingTypeParameter(...)]
to a component, the specified generic type argument is automatically used by descendants that:
- Are nested as child content for the component in the same
.razor
document. - Also declare a
@typeparam
with the exact same name. - Don't have another value explicitly supplied or implicitly inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type.
When receiving a cascaded type parameter, components obtain the parameter value from the closest ancestor that has a CascadingTypeParameterAttribute with a matching name. Cascaded generic type parameters are overridden within a particular subtree.
Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example T
or TItem
. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
Generic types can be cascaded to child components in either of the following approaches with ancestor (parent) components, which are demonstrated in the following two sub-sections:
- Explicitly set the cascaded generic type.
- Infer the cascaded generic type.
The following subsections provide examples of the preceding approaches using the following two ListDisplay
components. The components receive and render list data and are generically typed as TExample
. These components are for demonstration purposes and only differ in the color of text that the list is rendered. If you wish to experiment with the components in the following sub-sections in a local test app, add the following two components to the app first.
Shared/ListDisplay1.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:blue">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListDisplay2.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:red">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
}
Explicit generic types based on ancestor components
The demonstration in this section cascades a type explicitly for TExample
.
Note
This section uses the two ListDisplay
components in the Cascaded generic type support section.
The following ListGenericTypeItems2
component receives data and cascades a generic type parameter named TExample
to its descendent components. In the upcoming parent component, the ListGenericTypeItems2
component is used to display list data with the preceding ListDisplay
component.
Shared/ListGenericTypeItems2.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 2</h2>
@ChildContent
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
The following GenericTypeExample2
parent component sets the child content (RenderFragment) of two ListGenericTypeItems2
components specifying the ListGenericTypeItems2
types (TExample
), which are cascaded to child components. ListDisplay
components are rendered with the list item data shown in the example. String data is used with the first ListGenericTypeItems2
component, and integer data is used with the second ListGenericTypeItems2
component.
Pages/GenericTypeExample2.razor
:
@page "/generic-type-example-2"
<h1>Generic Type Example 2</h1>
<ListGenericTypeItems2 TExample="string">
<ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
<ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems2>
<ListGenericTypeItems2 TExample="int">
<ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
<ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems2>
Specifying the type explicitly also allows the use of cascading values and parameters to provide data to child components, as the following demonstration shows.
Shared/ListDisplay3.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:blue">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListDisplay4.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul style="color:red">
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
}
Shared/ListGenericTypeItems3.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 3</h2>
@ChildContent
@if (ExampleList is not null)
{
<ul style="color:green">
@foreach(var item in ExampleList)
{
<li>@item</li>
}
</ul>
<p>
Type of <code>TExample</code>: @typeof(TExample)
</p>
}
@code {
[CascadingParameter]
protected IEnumerable<TExample>? ExampleList { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
When cascading the data in the following example, the type must be provided to the ListGenericTypeItems3
component.
Pages/GenericTypeExample3.razor
:
@page "/generic-type-example-3"
<h1>Generic Type Example 3</h1>
<CascadingValue Value="@stringData">
<ListGenericTypeItems3 TExample="string">
<ListDisplay3 />
<ListDisplay4 />
</ListGenericTypeItems3>
</CascadingValue>
<CascadingValue Value="@integerData">
<ListGenericTypeItems3 TExample="int">
<ListDisplay3 />
<ListDisplay4 />
</ListGenericTypeItems3>
</CascadingValue>
@code {
private List<string> stringData = new() { "Item 1", "Item 2" };
private List<int> integerData = new() { 1, 2, 3 };
}
When multiple generic types are cascaded, values for all generic types in the set must be passed. In the following example, TItem
, TValue
, and TEdit
are GridColumn
generic types, but the parent component that places GridColumn
doesn't specify the TItem
type:
<GridColumn TValue="string" TEdit="@TextEdit" />
The preceding example generates a compile-time error that the GridColumn
component is missing the TItem
type parameter. Valid code specifies all of the types:
<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" />
Infer generic types based on ancestor components
The demonstration in this section cascades a type inferred for TExample
.
Note
This section uses the two ListDisplay
components in the Cascaded generic type support section.
Shared/ListGenericTypeItems4.razor
:
@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample
<h2>List Generic Type Items 4</h2>
@ChildContent
@if (ExampleList is not null)
{
<ul style="color:green">
@foreach(var item in ExampleList)
{
<li>@item</li>
}
</ul>
<p>
Type of <code>TExample</code>: @typeof(TExample)
</p>
}
@code {
[Parameter]
public IEnumerable<TExample>? ExampleList { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
The following GenericTypeExample4
component with inferred cascaded types provides different data for display.
Pages/GenericTypeExample4.razor
:
@page "/generic-type-example-4"
<h1>Generic Type Example 4</h1>
<ListGenericTypeItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
<ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
<ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems4>
<ListGenericTypeItems4 ExampleList="@(new List<int> { 7, 8, 9 })">
<ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
<ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems4>
The following GenericTypeExample5
component with inferred cascaded types provides the same data for display. The following example directly assigns the data to the components.
Pages/GenericTypeExample5.razor
:
@page "/generic-type-example-5"
<h1>Generic Type Example 5</h1>
<ListGenericTypeItems4 ExampleList="@stringData">
<ListDisplay1 ExampleList="@stringData" />
<ListDisplay2 ExampleList="@stringData" />
</ListGenericTypeItems4>
<ListGenericTypeItems4 ExampleList="@integerData">
<ListDisplay1 ExampleList="@integerData" />
<ListDisplay2 ExampleList="@integerData" />
</ListGenericTypeItems4>
@code {
private List<string> stringData = new() { "Item 1", "Item 2" };
private List<int> integerData = new() { 1, 2, 3 };
}
Render static root Razor components
A root Razor component is the first component loaded of any component hierarchy created by the app.
In an app created from the Blazor Server project template, the App
component (App.razor
) is created as the default root component in Pages/_Host.cshtml
using the Component Tag Helper:
<component type="typeof(App)" render-mode="ServerPrerendered" />
In an app created from the Blazor WebAssembly project template, the App
component (App.razor
) is created as the default root component in Program.cs
:
builder.RootComponents.Add<App>("#app");
In the preceding code, the CSS selector, #app
, indicates that the App
component is created for the <div>
in wwwroot/index.html
with an id
of app
:
<div id="app">...</app>
MVC and Razor Pages apps can also use the Component Tag Helper to register statically-rendered Blazor WebAssembly root components:
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
Statically-rendered components can only be added to the app. They can't be removed or updated afterwards.
For more information, see the following resources:
Blazor apps are built using Razor components, informally known as Blazor components. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and used in MVC and Razor Pages apps.
Component classes
Components are implemented using a combination of C# and HTML markup in Razor component files with the .razor
file extension.
By default, ComponentBase is the base class for components described by Razor component files. ComponentBase implements the lowest abstraction of components, the IComponent interface. ComponentBase defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events.
ComponentBase
in dotnet/aspnetcore
reference source: The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice.
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Developers typically create Razor components from Razor component files (.razor
) or base their components on ComponentBase, but components can also be built by implementing IComponent. Developer-built components that implement IComponent can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain.
Razor syntax
Components use Razor syntax. Two Razor features are extensively used by components, directives and directive attributes. These are reserved keywords prefixed with @
that appear in Razor markup:
- Directives: Change the way component markup is parsed or functions. For example, the
@page
directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. - Directive attributes: Change the way a component element is parsed or functions. For example, the
@bind
directive attribute for an<input>
element binds data to the element's value.
Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see Razor syntax reference for ASP.NET Core.
Names
A component's name must start with an uppercase character:
ProductDetail.razor
is valid.productDetail.razor
is invalid.
Common Blazor naming conventions used throughout the Blazor documentation include:
- Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example,
Pages/ProductDetail.razor
indicates that theProductDetail
component has a file name ofProductDetail.razor
and resides in thePages
folder of the app. - Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a
ProductDetail
component with a route template of/product-detail
(@page "/product-detail"
) is requested in a browser at the relative URL/product-detail
.
†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word.
Routing
Routing in Blazor is achieved by providing a route template to each accessible component in the app with an @page
directive. When a Razor file with an @page
directive is compiled, the generated class is given a RouteAttribute specifying the route template. At runtime, the router searches for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL.
The following HelloWorld
component uses a route template of /hello-world
. The rendered webpage for the component is reached at the relative URL /hello-world
. When running a Blazor app locally with the default protocol, host, and port, the HelloWorld
component is requested in the browser at https://localhost:5001/hello-world
. Components that produce webpages usually reside in the Pages
folder, but you can use any folder to hold components, including within nested folders.
Pages/HelloWorld.razor
:
@page "/hello-world"
<h1>Hello World!</h1>
The preceding component loads in the browser at /hello-world
regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the NavMenu
component so that a link to the component appears in the app's UI-based navigation.
For the preceding HelloWorld
component, you can add a NavLink
component to the NavMenu
component in the Shared
folder. For more information, including descriptions of the NavLink
and NavMenu
components, see ASP.NET Core Blazor routing and navigation.
Markup
A component's UI is defined using Razor syntax, which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file.
Members of the component class are defined in one or more @code
blocks. In @code
blocks, component state is specified and processed with C#:
- Property and field initializers.
- Parameter values from arguments passed by parent components and route parameters.
- Methods for user event handling, lifecycle events, and custom component logic.
Component members are used in rendering logic using C# expressions that start with the @
symbol. For example, a C# field is rendered by prefixing @
to the field name. The following Markup
component evaluates and renders:
headingFontStyle
for the CSS property valuefont-style
of the heading element.headingText
for the content of the heading element.
Pages/Markup.razor
:
@page "/markup"
<h1 style="font-style:@headingFontStyle">@headingText</h1>
@code {
private string headingFontStyle = "italic";
private string headingText = "Put on your new Blazor!";
}
Note
Examples throughout the Blazor documentation specify the private
access modifier for private members. Private members are scoped to a component's class. However, C# assumes the private
access modifier when no access modifier is present, so explicitly marking members "private
" in your own code is optional. For more information on access modifiers, see Access Modifiers (C# Programming Guide).
The Blazor framework processes a component internally as a render tree, which is the combination of a component's Document Object Model (DOM) and Cascading Style Sheet Object Model (CSSOM). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see ASP.NET Core Razor component rendering.
Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: @if
, @code
, @bind
). Property names are uppercase (example: @Body
for LayoutComponentBase.Body).
Asynchronous methods (async
) don't support returning void
The Blazor framework doesn't track void
-returning asynchronous methods (async
). As a result, exceptions aren't caught if void
is returned. Always return a Task from asynchronous methods.
Nested components
Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type.
Consider the following Heading
component, which can be used by other components to display a heading.
Shared/Heading.razor
:
<h1 style="font-style:@headingFontStyle">Heading Example</h1>
@code {
private string headingFontStyle = "italic";
}
The following markup in the HeadingExample
component renders the preceding Heading
component at the location where the <Heading />
tag appears.
Pages/HeadingExample.razor
:
@page "/heading-example"
<Heading />
If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an @using
directive for the component's namespace makes the component available, which resolves the warning. For more information, see the Class name and namespace section.
The Heading
component example shown in this section doesn't have an @page
directive, so the Heading
component isn't directly accessible to a user via a direct request in the browser. However, any component with an @page
directive can be nested in another component. If the Heading
component was directly accessible by including @page "/heading"
at the top of its Razor file, then the component would be rendered for browser requests at both /heading
and /heading-example
.
Class name and namespace
Components are ordinary C# classes and can be placed anywhere within a project. Components that produce webpages usually reside in the Pages
folder. Non-page components are frequently placed in the Shared
folder or a custom folder added to the project.
Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is BlazorSample
and the Counter
component resides in the Pages
folder:
- The
Counter
component's namespace isBlazorSample.Pages
. - The fully qualified type name of the component is
BlazorSample.Pages.Counter
.
For custom folders that hold components, add an @using
directive to the parent component or to the app's _Imports.razor
file. The following example makes components in the Components
folder available:
@using BlazorSample.Components
Note
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
).
Components can also be referenced using their fully qualified names, which doesn't require an @using
directive. The following example directly references the ProductDetail
component in the Components
folder of the app:
<BlazorSample.Components.ProductDetail />
The namespace of a component authored with Razor is based on the following (in priority order):
- The
@namespace
directive in the Razor file's markup (for example,@namespace BlazorSample.CustomNamespace
). - The project's
RootNamespace
in the project file (for example,<RootNamespace>BlazorSample</RootNamespace>
). - The project name, taken from the project file's file name (
.csproj
), and the path from the project root to the component. For example, the framework resolves{PROJECT ROOT}/Pages/Index.razor
with a project namespace ofBlazorSample
(BlazorSample.csproj
) to the namespaceBlazorSample.Pages
for theIndex
component.{PROJECT ROOT}
is the project root path. Components follow C# name binding rules. For theIndex
component in this example, the components in scope are all of the components:- In the same folder,
Pages
. - The components in the project's root that don't explicitly specify a different namespace.
- In the same folder,
The following are not supported:
- The
global::
qualification. - Importing components with aliased
using
statements. For example,@using Foo = Bar
isn't supported. - Partially-qualified names. For example, you can't add
@using BlazorSample
to a component and then reference theNavMenu
component in the app'sShared
folder (Shared/NavMenu.razor
) with<Shared.NavMenu></Shared.NavMenu>
.
Partial class support
Components are generated as C# partial classes and are authored using either of the following approaches:
- A single file contains C# code defined in one or more
@code
blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. - HTML and Razor markup are placed in a Razor file (
.razor
). C# code is placed in a code-behind file defined as a partial class (.cs
).
Note
A component stylesheet that defines component-specific styles is a separate file (.css
). Blazor CSS isolation is described later in ASP.NET Core Blazor CSS isolation.
The following example shows the default Counter
component with an @code
block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring.
Pages/Counter.razor
:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The following Counter
component splits HTML and Razor markup from C# code using a code-behind file with a partial class:
Pages/CounterPartialClass.razor
:
@page "/counter-partial-class"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Pages/CounterPartialClass.razor.cs
:
namespace BlazorSample.Pages
{
public partial class CounterPartialClass
{
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
}
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
). Add namespaces to a partial class file as needed.
Typical namespaces used by components:
using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;
Typical namespaces also include the namespace of the app and the namespace corresponding to the app's Shared
folder:
using BlazorSample;
using BlazorSample.Shared;
Specify a base class
The @inherits
directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The BlazorRocksBase
base class derives from ComponentBase.
Pages/BlazorRocks.razor
:
@page "/blazor-rocks"
@inherits BlazorRocksBase
<h1>@BlazorRocksText</h1>
BlazorRocksBase.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample
{
public class BlazorRocksBase : ComponentBase
{
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
}
}
Component parameters
Component parameters pass data to components and are defined using public C# properties on the component class with the [Parameter]
attribute. In the following example, a built-in reference type (System.String) and a user-defined reference type (PanelBody
) are passed as component parameters.
PanelBody.cs
:
public class PanelBody
{
public string Text { get; set; }
public string Style { get; set; }
}
Shared/ParameterChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">@Title</div>
<div class="card-body" style="font-style:@Body.Style">
@Body.Text
</div>
</div>
@code {
[Parameter]
public string Title { get; set; } = "Set By Child";
[Parameter]
public PanelBody Body { get; set; } =
new()
{
Text = "Set by child.",
Style = "normal"
};
}
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
The Title
and Body
component parameters of the ParameterChild
component are set by arguments in the HTML tag that renders the instance of the component. The following ParameterParent
component renders two ParameterChild
components:
- The first
ParameterChild
component is rendered without supplying parameter arguments. - The second
ParameterChild
component receives values forTitle
andBody
from theParameterParent
component, which uses an explicit C# expression to set the values of thePanelBody
's properties.
Pages/ParameterParent.razor
:
@page "/parameter-parent"
<h1>Child component (without attribute values)</h1>
<ParameterChild />
<h1>Child component (with attribute values)</h1>
<ParameterChild Title="Set by Parent"
Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />
The following rendered HTML markup from the ParameterParent
component shows ParameterChild
component default values when the ParameterParent
component doesn't supply component parameter values. When the ParameterParent
component provides component parameter values, they replace the ParameterChild
component's default values.
Note
For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup.
<h1>Child component (without attribute values)</h1>
<div>
<div>Set By Child</div>
<div>Set by child.</div>
</div>
<h1>Child component (with attribute values)</h1>
<div>
<div>Set by Parent</div>
<div>Set by parent.</div>
</div>
Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using Razor's reserved @
symbol. The following ParameterParent2
component displays four instances of the preceding ParameterChild
component and sets their Title
parameter values to:
- The value of the
title
field. - The result of the
GetTitle
C# method. - The current local date in long format with ToLongDateString, which uses an implicit C# expression.
- The
panelData
object'sTitle
property.
The @
prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set.
Outside of string parameters, we recommend the use of the @
prefix for nonliterals, even when they aren't strictly required.
We don't recommend the use of the @
prefix for literals (for example, boolean values), keywords (for example, this
), or null
, but you can choose to use them if you wish. For example, IsFixed="@true"
is uncommon but supported.
Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, Value=this
is supported, instead of Value="this"
. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies.
Throughout the documentation, code examples:
- Always use quotes. Example:
Value="this"
. - Nonliterals always use the
@
prefix, even when it's optional. Examples:Title="@title"
, wheretitle
is a string-typed variable.Count="@ct"
, wherect
is a number-typed variable. - Literals, outside of Razor expressions, always avoid
@
. Example:IsFixed="true"
.
Pages/ParameterParent2.razor
:
@page "/parameter-parent-2"
<ParameterChild Title="@title" />
<ParameterChild Title="@GetTitle()" />
<ParameterChild Title="@DateTime.Now.ToLongDateString()" />
<ParameterChild Title="@panelData.Title" />
@code {
private string title = "From Parent field";
private PanelData panelData = new();
private string GetTitle()
{
return "From Parent method";
}
private class PanelData
{
public string Title { get; set; } = "From Parent object";
}
}
Note
When assigning a C# member to a component parameter, prefix the member with the @
symbol and never prefix the parameter's HTML attribute.
Correct:
<ParameterChild Title="@title" />
Incorrect:
<ParameterChild @Title="title" />
Unlike in Razor pages (.cshtml
), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the asynchronous lifecycle events. After each asynchronous lifecycle event, the component may render again. The following Razor syntax is not supported:
<ParameterChild Title="@await ..." />
The code in the preceding example generates a compiler error when the app is built:
The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
To obtain a value for the Title
parameter in the preceding example asynchronously, the component can use the OnInitializedAsync
lifecycle event, as the following example demonstrates:
<ParameterChild Title="@title" />
@code {
private string title;
protected override async Task OnInitializedAsync()
{
title = await ...;
}
}
For more information, see ASP.NET Core Razor component lifecycle.
Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is not supported. The following example seeks to concatenate the text "Set by
" with an object's property value. Although this syntax is supported in a Razor page (.cshtml
), it isn't valid for assignment to the child's Title
parameter in a component. The following Razor syntax is not supported:
<ParameterChild Title="Set by @(panelData.Title)" />
The code in the preceding example generates a compiler error when the app is built:
Component attributes do not support complex content (mixed C# and markup).
To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "Set by
" and an object's property value in the C# method GetTitle
:
Pages/ParameterParent3.razor
:
@page "/parameter-parent-3"
<ParameterChild Title="@GetTitle()" />
@code {
private PanelData panelData = new();
private string GetTitle() => $"Set by {panelData.Title}";
private class PanelData
{
public string Title { get; set; } = "Parent";
}
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
Component parameters should be declared as auto-properties, meaning that they shouldn't contain custom logic in their get
or set
accessors. For example, the following StartData
property is an auto-property:
[Parameter]
public DateTime StartData { get; set; }
Don't place custom logic in the get
or set
accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a set
accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results.
To transform a received parameter value:
- Leave the parameter property as an auto-property to represent the supplied raw data.
- Create a different property or method to supply the transformed data based on the parameter property.
Override OnParametersSetAsync
to transform a received parameter each time new data is received.
Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local DateTime with DateTime.Now to StartData
is valid syntax in a component:
[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;
After the initial assignment of DateTime.Now, do not assign a value to StartData
in developer code. For more information, see the Overwritten parameters section of this article.
Route parameters
Components can specify route parameters in the route template of the @page
directive. The Blazor router uses route parameters to populate corresponding component parameters.
Optional route parameters are supported. In the following example, the text
optional parameter assigns the value of the route segment to the component's Text
property. If the segment isn't present, the value of Text
is set to "fantastic
" in the OnInitialized
lifecycle method.
Pages/RouteParameter.razor
:
@page "/route-parameter/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
For information on catch-all route parameters ({*pageRoute}
), which capture paths across multiple folder boundaries, see ASP.NET Core Blazor routing and navigation.
Child content render fragments
Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags.
In the following example, the RenderFragmentChild
component has a ChildContent
component parameter that represents a segment of the UI to render as a RenderFragment. The position of ChildContent
in the component's Razor markup is where the content is rendered in the final HTML output.
Shared/RenderFragmentChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">Child content</div>
<div class="card-body">@ChildContent</div>
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
}
Important
The property receiving the RenderFragment content must be named ChildContent
by convention.
Event callbacks aren't supported for RenderFragment.
The following RenderFragmentParent
component provides content for rendering the RenderFragmentChild
by placing the content inside the child component's opening and closing tags.
Pages/RenderFragmentParent.razor
:
@page "/render-fragment-parent"
<h1>Render child content</h1>
<RenderFragmentChild>
Content of the child component is supplied
by the parent component.
</RenderFragmentChild>
Due to the way that Blazor renders child content, rendering components inside a for
loop requires a local index variable if the incrementing loop variable is used in the RenderFragmentChild
component's content. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Three children with an index variable</h1>
@for (int c = 0; c < 3; c++)
{
var current = c;
<RenderFragmentChild>
Count: @current
</RenderFragmentChild>
}
Alternatively, use a foreach
loop with Enumerable.Range instead of a for
loop. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Second example of three children with an index variable</h1>
@foreach (var c in Enumerable.Range(0,3))
{
<RenderFragmentChild>
Count: @c
</RenderFragmentChild>
}
Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections:
- Blazor layouts
- Pass data across a component hierarchy
- Templated components
- Global exception handling
Note
Blazor framework's built-in Razor components use the same ChildContent
component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name ChildContent
in the API documentation (filters API with the search term "ChildContent").
Render fragments for reusable rendering logic
You can factor out child components purely as a way of reusing rendering logic. In any component's @code
block, define a RenderFragment and render the fragment from any location as many times as needed:
<h1>Hello, world!</h1>
@RenderWelcomeInfo
<p>Render the welcome info a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = __builder =>
{
<p>Welcome to your new app!</p>
};
}
For more information, see Reuse rendering logic.
Overwritten parameters
The Blazor framework generally imposes safe parent-to-child parameter assignment:
- Parameters aren't overwritten unexpectedly.
- Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops.
A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:
- The child component is rendered with one or more parameter values from the parent component.
- The child writes directly to the value of a parameter.
- The parent component rerenders and overwrites the value of the child's parameter.
The potential for overwriting parameter values extends into the child component's property set
accessors, too.
Important
Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time.
Consider the following Expander
component that:
- Renders child content.
- Toggles showing child content with a component parameter (
Expanded
).
After the following Expander
component demonstrates an overwritten parameter, a modified Expander
component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>
@if (Expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
[Parameter]
public bool Expanded { private get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private void Toggle()
{
Expanded = !Expanded;
}
}
The Expander
component is added to the following ExpanderExample
parent component that may call StateHasChanged:
- Calling StateHasChanged in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. StateHasChanged is covered in more detail later in ASP.NET Core Razor component lifecycle and ASP.NET Core Razor component rendering.
- The button's
@onclick
directive attribute attaches an event handler to the button'sonclick
event. Event handling is covered in more detail later in ASP.NET Core Blazor event handling.
Pages/ExpanderExample.razor
:
@page "/expander-example"
<Expander Expanded="true">
Expander 1 content
</Expander>
<Expander Expanded="true" />
<button @onclick="StateHasChanged">
Call StateHasChanged
</button>
Initially, the Expander
components behave independently when their Expanded
properties are toggled. The child components maintain their states as expected.
If StateHasChanged is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed:
- For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed.
- For unchecked parameter types, Blazor rerenders the child component regardless of whether or not the parameters have changed. Child content falls into this category of parameter types because child content is of type RenderFragment, which is a delegate that refers to other mutable objects.
For the ExpanderExample
component:
- The first
Expander
component sets child content in a potentially mutable RenderFragment, so a call to StateHasChanged in the parent component automatically rerenders the component and potentially overwrites the value ofExpanded
to its initial value oftrue
. - The second
Expander
component doesn't set child content. Therefore, a potentially mutable RenderFragment doesn't exist. A call to StateHasChanged in the parent component doesn't automatically rerender the child component, so the component'sExpanded
value isn't overwritten.
To maintain state in the preceding scenario, use a private field in the Expander
component to maintain its toggled state.
The following revised Expander
component:
- Accepts the
Expanded
component parameter value from the parent. - Assigns the component parameter value to a private field (
expanded
) in theOnInitialized
event. - Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter.
Note
The advice in this section extends to similar logic in component parameter set
accessors, which can result in similar undesirable side effects.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>
@if (expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
private bool expanded;
[Parameter]
public bool Expanded { private get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
protected override void OnInitialized()
{
expanded = Expanded;
}
private void Toggle()
{
expanded = !expanded;
}
}
For additional information, see Blazor Two Way Binding Error (dotnet/aspnetcore #24599).
For more information on change detection, including information on the exact types that Blazor checks, see ASP.NET Core Razor component rendering.
Attribute splatting and arbitrary parameters
Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes
Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an <input>
that supports many parameters.
In the following Splat
component:
- The first
<input>
element (id="useIndividualParams"
) uses individual component parameters. - The second
<input>
element (id="useAttributesDict"
) uses attribute splatting.
Pages/Splat.razor
:
@page "/splat"
<input id="useIndividualParams"
maxlength="@maxlength"
placeholder="@placeholder"
required="@required"
size="@size" />
<input id="useAttributesDict"
@attributes="InputAttributes" />
@code {
private string maxlength = "10";
private string placeholder = "Input placeholder text";
private string required = "required";
private string size = "50";
private Dictionary<string, object> InputAttributes { get; set; } =
new()
{
{ "maxlength", "10" },
{ "placeholder", "Input placeholder text" },
{ "required", "required" },
{ "size", "50" }
};
}
The rendered <input>
elements in the webpage are identical:
<input id="useIndividualParams"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
<input id="useAttributesDict"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
To accept arbitrary attributes, define a component parameter with the CaptureUnmatchedValues property set to true
:
@code {
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> InputAttributes { get; set; }
}
The CaptureUnmatchedValues property on [Parameter]
allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with CaptureUnmatchedValues. The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object>
with string keys. Use of IEnumerable<KeyValuePair<string, object>>
or IReadOnlyDictionary<string, object>
are also options in this scenario.
The position of @attributes
relative to the position of element attributes is important. When @attributes
are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component:
Shared/AttributeOrderChild1.razor
:
<div @attributes="AdditionalAttributes" extra="5" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent1.razor
:
@page "/attribute-order-parent-1"
<AttributeOrderChild1 extra="10" />
The AttributeOrderChild1
component's extra
attribute is set to the right of @attributes
. The AttributeOrderParent1
component's rendered <div>
contains extra="5"
when passed through the additional attribute because the attributes are processed right to left (last to first):
<div extra="5" />
In the following example, the order of extra
and @attributes
is reversed in the child component's <div>
:
Shared/AttributeOrderChild2.razor
:
<div extra="5" @attributes="AdditionalAttributes" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent2.razor
:
@page "/attribute-order-parent-2"
<AttributeOrderChild2 extra="10" />
The <div>
in the parent component's rendered webpage contains extra="10"
when passed through the additional attribute:
<div extra="10" />
Capture references to components
Component references provide a way to reference a component instance for issuing commands. To capture a component reference:
- Add an
@ref
attribute to the child component. - Define a field with the same type as the child component.
When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance.
Consider the following ReferenceChild
component that logs a message when its ChildMethod
is called.
Shared/ReferenceChild.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger
@code {
public void ChildMethod(int value)
{
logger.LogInformation("Received {Value} in ChildMethod", value);
}
}
A component reference is only populated after the component is rendered and its output includes ReferenceChild
's element. Until the component is rendered, there's nothing to reference.
To manipulate component references after the component has finished rendering, use the OnAfterRender
or OnAfterRenderAsync
methods.
To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the OnAfterRender
or OnAfterRenderAsync
methods. This ensures that the reference variable is assigned before the event handler is assigned.
The following lambda approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent1.razor
:
@page "/reference-parent-1"
<button @onclick="@(() => childComponent.ChildMethod(5))">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild childComponent;
}
The following delegate approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent2.razor
:
@page "/reference-parent-2"
<button @onclick="callChildMethod">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild childComponent;
private Action callChildMethod;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
callChildMethod = CallChildMethod;
}
}
private void CallChildMethod()
{
childComponent.ChildMethod(5);
}
}
While capturing component references use a similar syntax to capturing element references, capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code.
Important
Do not use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the component parameters section and the ASP.NET Core Blazor data binding article.
Synchronization context
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and event callbacks raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently.
Avoid thread-blocking calls
Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying Task is complete:
Note
Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling Thread.Sleep.
Invoke component methods externally to update state
In the event a component must be updated based on an external event, such as a timer or other notification, use the InvokeAsync
method, which dispatches code execution to Blazor's synchronization context. For example, consider the following notifier service that can notify any listening component about updated state. The Update
method can be called from anywhere in the app.
TimerService.cs
:
using System;
using System.Timers;
using Microsoft.Extensions.Logging;
public class TimerService : IDisposable
{
private int elapsedCount;
private readonly ILogger<TimerService> logger;
private readonly NotifierService notifier;
private Timer timer;
public TimerService(NotifierService notifier, ILogger<TimerService> logger)
{
this.notifier = notifier;
this.logger = logger;
}
public void Start()
{
if (timer is null)
{
timer = new();
timer.AutoReset = true;
timer.Interval = 10000;
timer.Elapsed += HandleTimer;
timer.Enabled = true;
logger.LogInformation("Started");
}
}
private async void HandleTimer(object source, ElapsedEventArgs e)
{
elapsedCount += 1;
await notifier.Update("elapsedCount", elapsedCount);
logger.LogInformation($"elapsedCount: {elapsedCount}");
}
public void Dispose()
{
timer?.Dispose();
}
}
NotifierService.cs
:
using System;
using System.Threading.Tasks;
public class NotifierService
{
public async Task Update(string key, int value)
{
if (Notify != null)
{
await Notify.Invoke(key, value);
}
}
public event Func<string, int, Task> Notify;
}
Register the services:
In a Blazor WebAssembly app, register the services as singletons in
Program.cs
:builder.Services.AddSingleton<NotifierService>(); builder.Services.AddSingleton<TimerService>();
In a Blazor Server app, register the services as scoped in
Startup.ConfigureServices
:services.AddScoped<NotifierService>(); services.AddScoped<TimerService>();
Use the NotifierService
to update a component.
Pages/ReceiveNotifications.razor
:
@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer
<h1>Receive Notifications</h1>
<h2>Timer Service</h2>
<button @onclick="StartTimer">Start Timer</button>
<h2>Notifications</h2>
<p>
Status:
@if (lastNotification.key is not null)
{
<span>@lastNotification.key = @lastNotification.value</span>
}
else
{
<span>Awaiting first notification</span>
}
</p>
@code {
private (string key, int value) lastNotification;
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public async Task OnNotify(string key, int value)
{
await InvokeAsync(() =>
{
lastNotification = (key, value);
StateHasChanged();
});
}
private void StartTimer()
{
Timer.Start();
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
In the preceding example:
NotifierService
invokes the component'sOnNotify
method outside of Blazor's synchronization context.InvokeAsync
is used to switch to the correct context and queue a render. For more information, see ASP.NET Core Razor component rendering.- The component implements IDisposable. The
OnNotify
delegate is unsubscribed in theDispose
method, which is called by the framework when the component is disposed. For more information, see ASP.NET Core Razor component lifecycle.
Important
If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (ExecutionContext) at the time the handler is registered. For more information, see Calling InvokeAsync(StateHasChanged)
causes page to fallback to default culture (dotnet/aspnetcore #28521).
Use @key
to control the preservation of elements and components
When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the @key
directive attribute is required.
Consider the following example that demonstrates a collection mapping problem that's solved by using @key
.
For following Details
and PeopleExample
components:
- The
Details
component receives data (Data
) from the parentPeopleExample
component, which is displayed in an<input>
element. Any given displayed<input>
element can receive the focus of the page from the user when they select one of the<input>
elements. - The
PeopleExample
component creates a list of person objects for display using theDetails
component. Every three seconds, a new person is added to the collection.
This demonstration allows you to:
- Select an
<input>
from among several renderedDetails
components. - Study the behavior of the page's focus as the people collection automatically grows.
Shared/Details.razor
:
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
In the following PeopleExample
component, each iteration of adding a person in OnTimerCallback
results in Blazor rebuilding the entire collection. The page's focus remains on the same index position of <input>
elements, so the focus shifts each time a person is added. Shifting the focus away from what the user selected isn't desirable behavior. After demonstrating the poor behavior with the following component, the @key
directive attribute is used to improve the user's experience.
Pages/PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
The contents of the people
collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the people
collection, the user's focus is lost.
The mapping process of elements or components to a collection can be controlled with the @key
directive attribute. Use of @key
guarantees the preservation of elements or components based on the key's value. If the Details
component in the preceding example is keyed on the person
item, Blazor ignores rerendering Details
components that haven't changed.
To modify the PeopleExample
component to use the @key
directive attribute with the people
collection, update the <Details>
element to the following:
<Details @key="person" Data="@person.Data" />
When the people
collection changes, the association between Details
instances and person
instances is retained. When a Person
is inserted at the beginning of the collection, one new Details
instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection.
Other collection updates exhibit the same behavior when the @key
directive attribute is used:
- If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged.
- If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI.
Important
Keys are local to each container element or component. Keys aren't compared globally across the document.
When to use @key
Typically, it makes sense to use @key
whenever a list is rendered (for example, in a foreach
block) and a suitable value exists to define the @key
.
You can also use @key
to preserve an element or component subtree when an object doesn't change, as the following examples show.
Example 1:
<li @key="person">
<input value="@person.Data" />
</li>
Example 2:
<div @key="person">
@* other HTML elements *@
</div>
If an person
instance changes, the @key
attribute directive forces Blazor to:
- Discard the entire
<li>
or<div>
and their descendants. - Rebuild the subtree within the UI with new elements and components.
This is useful to guarantee that no UI state is preserved when the collection changes within a subtree.
Scope of @key
The @key
attribute directive is scoped to its own siblings within its parent.
Consider the following example. The first
and second
keys are compared against each other within the same scope of the outer <div>
element:
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
The following example demonstrates first
and second
keys in their own scopes, unrelated to each other and without influence on each other. Each @key
scope only applies to its parent <div>
element, not across the parent <div>
elements:
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
For the Details
component shown earlier, the following examples render person
data within the same @key
scope and demonstrate typical use cases for @key
:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
The following examples only scope @key
to the <div>
or <li>
element that surrounds each Details
component instance. Therefore, person
data for each member of the people
collection is not keyed on each person
instance across the rendered Details
components. Avoid the following patterns when using @key
:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
When not to use @key
There's a performance cost when rendering with @key
. The performance cost isn't large, but only specify @key
if preserving the element or component benefits the app.
Even if @key
isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using @key
is control over how model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping.
Values to use for @key
Generally, it makes sense to supply one of the following values for @key
:
- Model object instances. For example, the
Person
instance (person
) was used in the earlier example. This ensures preservation based on object reference equality. - Unique identifiers. For example, unique identifiers can be based on primary key values of type
int
,string
, orGuid
.
Ensure that values used for @key
don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values.
Apply an attribute
Attributes can be applied to components with the @attribute
directive. The following example applies the [Authorize]
attribute to the component's class:
@page "/"
@attribute [Authorize]
Conditional HTML element attributes
HTML element attribute properties are conditionally set based on the .NET value. If the value is false
or null
, the property isn't set. If the value is true
, the property is set.
In the following example, IsCompleted
determines if the <input>
element's checked
property is set.
Pages/ConditionalAttribute.razor
:
@page "/conditional-attribute"
<label>
<input type="checkbox" checked="@IsCompleted" />
Is Completed?
</label>
<button @onclick="@(() => IsCompleted = !IsCompleted)">
Change IsCompleted
</button>
@code {
[Parameter]
public bool IsCompleted { get; set; }
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Some HTML attributes, such as aria-pressed
, don't function properly when the .NET type is a bool
. In those cases, use a string
type instead of a bool
.
Raw HTML
Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a MarkupString value. The value is parsed as HTML or SVG and inserted into the DOM.
Warning
Rendering raw HTML constructed from any untrusted source is a security risk and should always be avoided.
The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component.
Pages/MarkupStringExample.razor
:
@page "/markup-string-example"
@((MarkupString)myMarkup)
@code {
private string myMarkup =
"<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}
Razor templates
Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format:
@<{HTML tag}>...</{HTML tag}>
The following example illustrates how to specify RenderFragment and RenderFragment<TValue> values and render templates directly in a component. Render fragments can also be passed as arguments to templated components.
Pages/RazorTemplate.razor
:
@page "/razor-template"
@timeTemplate
@petTemplate(new Pet { Name = "Nutty Rex" })
@code {
private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;
private class Pet
{
public string Name { get; set; }
}
}
Rendered output of the preceding code:
<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>
Static assets
Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's web root
(wwwroot
) folder or folders under the wwwroot
folder.
Use a base-relative path (/
) to refer to the web root for a static asset. In the following example, logo.png
is physically located in the {PROJECT ROOT}/wwwroot/images
folder. {PROJECT ROOT}
is the app's project root.
<img alt="Company logo" src="/images/logo.png" />
Components do not support tilde-slash notation (~/
).
For information on setting an app's base path, see Host and deploy ASP.NET Core Blazor.
Tag Helpers aren't supported in components
Tag Helpers
aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead.
Scalable Vector Graphics (SVG) images
Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg
), are supported via the <img>
tag:
<img alt="Example image" src="image.svg" />
Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css
):
.element-class {
background-image: url("image.svg");
}
Whitespace rendering behavior
Unless the @preservewhitespace
directive is used with a value of true
, extra whitespace is removed by default if:
- Leading or trailing within an element.
- Leading or trailing within a RenderFragment/RenderFragment<TValue> parameter (for example, child content passed to another component).
- It precedes or follows a C# code block, such as
@if
or@foreach
.
Whitespace removal might affect the rendered output when using a CSS rule, such as white-space: pre
. To disable this performance optimization and preserve the whitespace, take one of the following actions:
- Add the
@preservewhitespace true
directive at the top of the Razor file (.razor
) to apply the preference to a specific component. - Add the
@preservewhitespace true
directive inside an_Imports.razor
file to apply the preference to a subdirectory or to the entire project.
In most cases, no action is required, as apps typically continue to behave normally (but faster). If stripping whitespace causes a rendering problem for a particular component, use @preservewhitespace true
in that component to disable this optimization.
Generic type parameter support
The @typeparam
directive declares a generic type parameter for the generated component class:
@typeparam TItem
In the following example, the ListGenericTypeItems1
component is generically typed as TExample
.
Shared/ListGenericTypeItems1.razor
:
@typeparam TExample
@if (ExampleList is not null)
{
<ul>
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample> ExampleList{ get; set; }
}
The following GenericTypeExample1
component renders two ListGenericTypeItems1
components:
- String or integer data is assigned to the
ExampleList
parameter of each component. - Type
string
orint
that matches the type of the assigned data is set for the type parameter (TExample
) of each component.
Pages/GenericTypeExample1.razor
:
@page "/generic-type-example-1"
<h1>Generic Type Example 1</h1>
<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })"
TExample="string" />
<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })"
TExample="int" />
For more information, see the following articles:
Blazor apps are built using Razor components, informally known as Blazor components. A component is a self-contained portion of user interface (UI) with processing logic to enable dynamic behavior. Components can be nested, reused, shared among projects, and used in MVC and Razor Pages apps.
Component classes
Components are implemented using a combination of C# and HTML markup in Razor component files with the .razor
file extension.
By default, ComponentBase is the base class for components described by Razor component files. ComponentBase implements the lowest abstraction of components, the IComponent interface. ComponentBase defines component properties and methods for basic functionality, for example, to process a set of built-in component lifecycle events.
ComponentBase
in dotnet/aspnetcore
reference source: The reference source contains additional remarks on the built-in lifecycle events. However, keep in mind that the internal implementations of component features are subject to change at any time without notice.
Note
Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
Developers typically create Razor components from Razor component files (.razor
) or base their components on ComponentBase, but components can also be built by implementing IComponent. Developer-built components that implement IComponent can take low-level control over rendering at the cost of having to manually trigger rendering with events and lifecycle methods that the developer must create and maintain.
Razor syntax
Components use Razor syntax. Two Razor features are extensively used by components, directives and directive attributes. These are reserved keywords prefixed with @
that appear in Razor markup:
- Directives: Change the way component markup is parsed or functions. For example, the
@page
directive specifies a routable component with a route template and can be reached directly by a user's request in the browser at a specific URL. - Directive attributes: Change the way a component element is parsed or functions. For example, the
@bind
directive attribute for an<input>
element binds data to the element's value.
Directives and directive attributes used in components are explained further in this article and other articles of the Blazor documentation set. For general information on Razor syntax, see Razor syntax reference for ASP.NET Core.
Names
A component's name must start with an uppercase character:
ProductDetail.razor
is valid.productDetail.razor
is invalid.
Common Blazor naming conventions used throughout the Blazor documentation include:
- Component file paths use Pascal case† and appear before showing component code examples. Paths indicate typical folder locations. For example,
Pages/ProductDetail.razor
indicates that theProductDetail
component has a file name ofProductDetail.razor
and resides in thePages
folder of the app. - Component file paths for routable components match their URLs with hyphens appearing for spaces between words in a component's route template. For example, a
ProductDetail
component with a route template of/product-detail
(@page "/product-detail"
) is requested in a browser at the relative URL/product-detail
.
†Pascal case (upper camel case) is a naming convention without spaces and punctuation and with the first letter of each word capitalized, including the first word.
Routing
Routing in Blazor is achieved by providing a route template to each accessible component in the app with an @page
directive. When a Razor file with an @page
directive is compiled, the generated class is given a RouteAttribute specifying the route template. At runtime, the router searches for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL.
The following HelloWorld
component uses a route template of /hello-world
. The rendered webpage for the component is reached at the relative URL /hello-world
. When running a Blazor app locally with the default protocol, host, and port, the HelloWorld
component is requested in the browser at https://localhost:5001/hello-world
. Components that produce webpages usually reside in the Pages
folder, but you can use any folder to hold components, including within nested folders.
Pages/HelloWorld.razor
:
@page "/hello-world"
<h1>Hello World!</h1>
The preceding component loads in the browser at /hello-world
regardless of whether or not you add the component to the app's UI navigation. Optionally, components can be added to the NavMenu
component so that a link to the component appears in the app's UI-based navigation.
For the preceding HelloWorld
component, you can add a NavLink
component to the NavMenu
component in the Shared
folder. For more information, including descriptions of the NavLink
and NavMenu
components, see ASP.NET Core Blazor routing and navigation.
Markup
A component's UI is defined using Razor syntax, which consists of Razor markup, C#, and HTML. When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. The name of the generated class matches the name of the file.
Members of the component class are defined in one or more @code
blocks. In @code
blocks, component state is specified and processed with C#:
- Property and field initializers.
- Parameter values from arguments passed by parent components and route parameters.
- Methods for user event handling, lifecycle events, and custom component logic.
Component members are used in rendering logic using C# expressions that start with the @
symbol. For example, a C# field is rendered by prefixing @
to the field name. The following Markup
component evaluates and renders:
headingFontStyle
for the CSS property valuefont-style
of the heading element.headingText
for the content of the heading element.
Pages/Markup.razor
:
@page "/markup"
<h1 style="font-style:@headingFontStyle">@headingText</h1>
@code {
private string headingFontStyle = "italic";
private string headingText = "Put on your new Blazor!";
}
Note
Examples throughout the Blazor documentation specify the private
access modifier for private members. Private members are scoped to a component's class. However, C# assumes the private
access modifier when no access modifier is present, so explicitly marking members "private
" in your own code is optional. For more information on access modifiers, see Access Modifiers (C# Programming Guide).
The Blazor framework processes a component internally as a render tree, which is the combination of a component's Document Object Model (DOM) and Cascading Style Sheet Object Model (CSSOM). After the component is initially rendered, the component's render tree is regenerated in response to events. Blazor compares the new render tree against the previous render tree and applies any modifications to the browser's DOM for display. For more information, see ASP.NET Core Razor component rendering.
Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: @if
, @code
, @bind
). Property names are uppercase (example: @Body
for LayoutComponentBase.Body).
Asynchronous methods (async
) don't support returning void
The Blazor framework doesn't track void
-returning asynchronous methods (async
). As a result, exceptions aren't caught if void
is returned. Always return a Task from asynchronous methods.
Nested components
Components can include other components by declaring them using HTML syntax. The markup for using a component looks like an HTML tag where the name of the tag is the component type.
Consider the following Heading
component, which can be used by other components to display a heading.
Shared/Heading.razor
:
<h1 style="font-style:@headingFontStyle">Heading Example</h1>
@code {
private string headingFontStyle = "italic";
}
The following markup in the HeadingExample
component renders the preceding Heading
component at the location where the <Heading />
tag appears.
Pages/HeadingExample.razor
:
@page "/heading-example"
<Heading />
If a component contains an HTML element with an uppercase first letter that doesn't match a component name within the same namespace, a warning is emitted indicating that the element has an unexpected name. Adding an @using
directive for the component's namespace makes the component available, which resolves the warning. For more information, see the Class name and namespace section.
The Heading
component example shown in this section doesn't have an @page
directive, so the Heading
component isn't directly accessible to a user via a direct request in the browser. However, any component with an @page
directive can be nested in another component. If the Heading
component was directly accessible by including @page "/heading"
at the top of its Razor file, then the component would be rendered for browser requests at both /heading
and /heading-example
.
Class name and namespace
Components are ordinary C# classes and can be placed anywhere within a project. Components that produce webpages usually reside in the Pages
folder. Non-page components are frequently placed in the Shared
folder or a custom folder added to the project.
Typically, a component's namespace is derived from the app's root namespace and the component's location (folder) within the app. If the app's root namespace is BlazorSample
and the Counter
component resides in the Pages
folder:
- The
Counter
component's namespace isBlazorSample.Pages
. - The fully qualified type name of the component is
BlazorSample.Pages.Counter
.
For custom folders that hold components, add an @using
directive to the parent component or to the app's _Imports.razor
file. The following example makes components in the Components
folder available:
@using BlazorSample.Components
Note
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
).
Components can also be referenced using their fully qualified names, which doesn't require an @using
directive. The following example directly references the ProductDetail
component in the Components
folder of the app:
<BlazorSample.Components.ProductDetail />
The namespace of a component authored with Razor is based on the following (in priority order):
- The
@namespace
directive in the Razor file's markup (for example,@namespace BlazorSample.CustomNamespace
). - The project's
RootNamespace
in the project file (for example,<RootNamespace>BlazorSample</RootNamespace>
). - The project name, taken from the project file's file name (
.csproj
), and the path from the project root to the component. For example, the framework resolves{PROJECT ROOT}/Pages/Index.razor
with a project namespace ofBlazorSample
(BlazorSample.csproj
) to the namespaceBlazorSample.Pages
for theIndex
component.{PROJECT ROOT}
is the project root path. Components follow C# name binding rules. For theIndex
component in this example, the components in scope are all of the components:- In the same folder,
Pages
. - The components in the project's root that don't explicitly specify a different namespace.
- In the same folder,
The following are not supported:
- The
global::
qualification. - Importing components with aliased
using
statements. For example,@using Foo = Bar
isn't supported. - Partially-qualified names. For example, you can't add
@using BlazorSample
to a component and then reference theNavMenu
component in the app'sShared
folder (Shared/NavMenu.razor
) with<Shared.NavMenu></Shared.NavMenu>
.
Partial class support
Components are generated as C# partial classes and are authored using either of the following approaches:
- A single file contains C# code defined in one or more
@code
blocks, HTML markup, and Razor markup. Blazor project templates define their components using this single-file approach. - HTML and Razor markup are placed in a Razor file (
.razor
). C# code is placed in a code-behind file defined as a partial class (.cs
).
The following example shows the default Counter
component with an @code
block in an app generated from a Blazor project template. Markup and C# code are in the same file. This is the most common approach taken in component authoring.
Pages/Counter.razor
:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The following Counter
component splits HTML and Razor markup from C# code using a code-behind file with a partial class:
Pages/CounterPartialClass.razor
:
@page "/counter-partial-class"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Pages/CounterPartialClass.razor.cs
:
namespace BlazorSample.Pages
{
public partial class CounterPartialClass
{
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
}
@using
directives in the _Imports.razor
file are only applied to Razor files (.razor
), not C# files (.cs
). Add namespaces to a partial class file as needed.
Typical namespaces used by components:
using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
Typical namespaces also include the namespace of the app and the namespace corresponding to the app's Shared
folder:
using BlazorSample;
using BlazorSample.Shared;
Specify a base class
The @inherits
directive is used to specify a base class for a component. The following example shows how a component can inherit a base class to provide the component's properties and methods. The BlazorRocksBase
base class derives from ComponentBase.
Pages/BlazorRocks.razor
:
@page "/blazor-rocks"
@inherits BlazorRocksBase
<h1>@BlazorRocksText</h1>
BlazorRocksBase.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample
{
public class BlazorRocksBase : ComponentBase
{
public string BlazorRocksText { get; set; } =
"Blazor rocks the browser!";
}
}
Component parameters
Component parameters pass data to components and are defined using public C# properties on the component class with the [Parameter]
attribute. In the following example, a built-in reference type (System.String) and a user-defined reference type (PanelBody
) are passed as component parameters.
PanelBody.cs
:
public class PanelBody
{
public string Text { get; set; }
public string Style { get; set; }
}
Shared/ParameterChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">@Title</div>
<div class="card-body" style="font-style:@Body.Style">
@Body.Text
</div>
</div>
@code {
[Parameter]
public string Title { get; set; } = "Set By Child";
[Parameter]
public PanelBody Body { get; set; } =
new PanelBody()
{
Text = "Set by child.",
Style = "normal"
};
}
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
The Title
and Body
component parameters of the ParameterChild
component are set by arguments in the HTML tag that renders the instance of the component. The following ParameterParent
component renders two ParameterChild
components:
- The first
ParameterChild
component is rendered without supplying parameter arguments. - The second
ParameterChild
component receives values forTitle
andBody
from theParameterParent
component, which uses an explicit C# expression to set the values of thePanelBody
's properties.
Pages/ParameterParent.razor
:
@page "/parameter-parent"
<h1>Child component (without attribute values)</h1>
<ParameterChild />
<h1>Child component (with attribute values)</h1>
<ParameterChild Title="Set by Parent"
Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />
The following rendered HTML markup from the ParameterParent
component shows ParameterChild
component default values when the ParameterParent
component doesn't supply component parameter values. When the ParameterParent
component provides component parameter values, they replace the ParameterChild
component's default values.
Note
For clarity, rendered CSS style classes aren't shown in the following rendered HTML markup.
<h1>Child component (without attribute values)</h1>
<div>
<div>Set By Child</div>
<div>Set by child.</div>
</div>
<h1>Child component (with attribute values)</h1>
<div>
<div>Set by Parent</div>
<div>Set by parent.</div>
</div>
Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value using Razor's reserved @
symbol. The following ParameterParent2
component displays four instances of the preceding ParameterChild
component and sets their Title
parameter values to:
- The value of the
title
field. - The result of the
GetTitle
C# method. - The current local date in long format with ToLongDateString, which uses an implicit C# expression.
- The
panelData
object'sTitle
property.
The @
prefix is required for string parameters. Otherwise, the framework assumes that a string literal is set.
Outside of string parameters, we recommend the use of the @
prefix for nonliterals, even when they aren't strictly required.
We don't recommend the use of the @
prefix for literals (for example, boolean values), keywords (for example, this
), or null
, but you can choose to use them if you wish. For example, IsFixed="@true"
is uncommon but supported.
Quotes around parameter attribute values are optional in most cases per the HTML5 specification. For example, Value=this
is supported, instead of Value="this"
. However, we recommend using quotes because it's easier to remember and widely adopted across web-based technologies.
Throughout the documentation, code examples:
- Always use quotes. Example:
Value="this"
. - Nonliterals always use the
@
prefix, even when it's optional. Examples:Title="@title"
, wheretitle
is a string-typed variable.Count="@ct"
, wherect
is a number-typed variable. - Literals, outside of Razor expressions, always avoid
@
. Example:IsFixed="true"
.
Pages/ParameterParent2.razor
:
@page "/parameter-parent-2"
<ParameterChild Title="@title" />
<ParameterChild Title="@GetTitle()" />
<ParameterChild Title="@DateTime.Now.ToLongDateString()" />
<ParameterChild Title="@panelData.Title" />
@code {
private string title = "From Parent field";
private PanelData panelData = new PanelData();
private string GetTitle()
{
return "From Parent method";
}
private class PanelData
{
public string Title { get; set; } = "From Parent object";
}
}
Note
When assigning a C# member to a component parameter, prefix the member with the @
symbol and never prefix the parameter's HTML attribute.
Correct:
<ParameterChild Title="@title" />
Incorrect:
<ParameterChild @Title="title" />
Unlike in Razor pages (.cshtml
), Blazor can't perform asynchronous work in a Razor expression while rendering a component. This is because Blazor is designed for rendering interactive UIs. In an interactive UI, the screen must always display something, so it doesn't make sense to block the rendering flow. Instead, asynchronous work is performed during one of the asynchronous lifecycle events. After each asynchronous lifecycle event, the component may render again. The following Razor syntax is not supported:
<ParameterChild Title="@await ..." />
The code in the preceding example generates a compiler error when the app is built:
The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
To obtain a value for the Title
parameter in the preceding example asynchronously, the component can use the OnInitializedAsync
lifecycle event, as the following example demonstrates:
<ParameterChild Title="@title" />
@code {
private string title;
protected override async Task OnInitializedAsync()
{
title = await ...;
}
}
For more information, see ASP.NET Core Razor component lifecycle.
Use of an explicit Razor expression to concatenate text with an expression result for assignment to a parameter is not supported. The following example seeks to concatenate the text "Set by
" with an object's property value. Although this syntax is supported in a Razor page (.cshtml
), it isn't valid for assignment to the child's Title
parameter in a component. The following Razor syntax is not supported:
<ParameterChild Title="Set by @(panelData.Title)" />
The code in the preceding example generates a compiler error when the app is built:
Component attributes do not support complex content (mixed C# and markup).
To support the assignment of a composed value, use a method, field, or property. The following example performs the concatenation of "Set by
" and an object's property value in the C# method GetTitle
:
Pages/ParameterParent3.razor
:
@page "/parameter-parent-3"
<ParameterChild Title="@GetTitle()" />
@code {
private PanelData panelData = new PanelData();
private string GetTitle() => $"Set by {panelData.Title}";
private class PanelData
{
public string Title { get; set; } = "Parent";
}
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article.
Component parameters should be declared as auto-properties, meaning that they shouldn't contain custom logic in their get
or set
accessors. For example, the following StartData
property is an auto-property:
[Parameter]
public DateTime StartData { get; set; }
Don't place custom logic in the get
or set
accessor because component parameters are purely intended for use as a channel for a parent component to flow information to a child component. If a set
accessor of a child component property contains logic that causes rerendering of the parent component, an infinite rendering loop results.
To transform a received parameter value:
- Leave the parameter property as an auto-property to represent the supplied raw data.
- Create a different property or method to supply the transformed data based on the parameter property.
Override OnParametersSetAsync
to transform a received parameter each time new data is received.
Writing an initial value to a component parameter is supported because initial value assignments don't interfere with the Blazor's automatic component rendering. The following assignment of the current local DateTime with DateTime.Now to StartData
is valid syntax in a component:
[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;
After the initial assignment of DateTime.Now, do not assign a value to StartData
in developer code. For more information, see the Overwritten parameters section of this article.
Route parameters
Components can specify route parameters in the route template of the @page
directive. The Blazor router uses route parameters to populate corresponding component parameters.
Pages/RouteParameter.razor
:
@page "/route-parameter"
@page "/route-parameter/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
Optional route parameters aren't supported, so two @page
directives are applied in the preceding example. The first @page
directive permits navigation to the component without a route parameter. The second @page
directive receives the {text}
route parameter and assigns the value to the Text
property.
For information on catch-all route parameters ({*pageRoute}
), which capture paths across multiple folder boundaries, see ASP.NET Core Blazor routing and navigation.
Child content render fragments
Components can set the content of another component. The assigning component provides the content between the child component's opening and closing tags.
In the following example, the RenderFragmentChild
component has a ChildContent
component parameter that represents a segment of the UI to render as a RenderFragment. The position of ChildContent
in the component's Razor markup is where the content is rendered in the final HTML output.
Shared/RenderFragmentChild.razor
:
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">Child content</div>
<div class="card-body">@ChildContent</div>
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
}
Important
The property receiving the RenderFragment content must be named ChildContent
by convention.
Event callbacks aren't supported for RenderFragment.
The following RenderFragmentParent
component provides content for rendering the RenderFragmentChild
by placing the content inside the child component's opening and closing tags.
Pages/RenderFragmentParent.razor
:
@page "/render-fragment-parent"
<h1>Render child content</h1>
<RenderFragmentChild>
Content of the child component is supplied
by the parent component.
</RenderFragmentChild>
Due to the way that Blazor renders child content, rendering components inside a for
loop requires a local index variable if the incrementing loop variable is used in the RenderFragmentChild
component's content. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Three children with an index variable</h1>
@for (int c = 0; c < 3; c++)
{
var current = c;
<RenderFragmentChild>
Count: @current
</RenderFragmentChild>
}
Alternatively, use a foreach
loop with Enumerable.Range instead of a for
loop. The following example can be added to the preceding RenderFragmentParent
component:
<h1>Second example of three children with an index variable</h1>
@foreach (var c in Enumerable.Range(0,3))
{
<RenderFragmentChild>
Count: @c
</RenderFragmentChild>
}
Render fragments are used to render child content throughout Blazor apps and are described with examples in the following articles and article sections:
- Blazor layouts
- Pass data across a component hierarchy
- Templated components
- Global exception handling
Note
Blazor framework's built-in Razor components use the same ChildContent
component parameter convention to set their content. You can see the components that set child content by searching for the component parameter property name ChildContent
in the API documentation (filters API with the search term "ChildContent").
Render fragments for reusable rendering logic
You can factor out child components purely as a way of reusing rendering logic. In any component's @code
block, define a RenderFragment and render the fragment from any location as many times as needed:
<h1>Hello, world!</h1>
@RenderWelcomeInfo
<p>Render the welcome info a second time:</p>
@RenderWelcomeInfo
@code {
private RenderFragment RenderWelcomeInfo = __builder =>
{
<p>Welcome to your new app!</p>
};
}
For more information, see Reuse rendering logic.
Overwritten parameters
The Blazor framework generally imposes safe parent-to-child parameter assignment:
- Parameters aren't overwritten unexpectedly.
- Side effects are minimized. For example, additional renders are avoided because they may create infinite rendering loops.
A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:
- The child component is rendered with one or more parameter values from the parent component.
- The child writes directly to the value of a parameter.
- The parent component rerenders and overwrites the value of the child's parameter.
The potential for overwriting parameter values extends into the child component's property set
accessors, too.
Important
Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time.
Consider the following Expander
component that:
- Renders child content.
- Toggles showing child content with a component parameter (
Expanded
).
After the following Expander
component demonstrates an overwritten parameter, a modified Expander
component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>
@if (Expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
[Parameter]
public bool Expanded { private get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private void Toggle()
{
Expanded = !Expanded;
}
}
The Expander
component is added to the following ExpanderExample
parent component that may call StateHasChanged:
- Calling StateHasChanged in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. StateHasChanged is covered in more detail later in ASP.NET Core Razor component lifecycle and ASP.NET Core Razor component rendering.
- The button's
@onclick
directive attribute attaches an event handler to the button'sonclick
event. Event handling is covered in more detail later in ASP.NET Core Blazor event handling.
Pages/ExpanderExample.razor
:
@page "/expander-example"
<Expander Expanded="true">
Expander 1 content
</Expander>
<Expander Expanded="true" />
<button @onclick="StateHasChanged">
Call StateHasChanged
</button>
Initially, the Expander
components behave independently when their Expanded
properties are toggled. The child components maintain their states as expected.
If StateHasChanged is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed:
- For a group of parameter types that Blazor explicitly checks, Blazor rerenders a child component if it detects that any of the parameters have changed.
- For unchecked parameter types, Blazor rerenders the child component regardless of whether or not the parameters have changed. Child content falls into this category of parameter types because child content is of type RenderFragment, which is a delegate that refers to other mutable objects.
For the ExpanderExample
component:
- The first
Expander
component sets child content in a potentially mutable RenderFragment, so a call to StateHasChanged in the parent component automatically rerenders the component and potentially overwrites the value ofExpanded
to its initial value oftrue
. - The second
Expander
component doesn't set child content. Therefore, a potentially mutable RenderFragment doesn't exist. A call to StateHasChanged in the parent component doesn't automatically rerender the child component, so the component'sExpanded
value isn't overwritten.
To maintain state in the preceding scenario, use a private field in the Expander
component to maintain its toggled state.
The following revised Expander
component:
- Accepts the
Expanded
component parameter value from the parent. - Assigns the component parameter value to a private field (
expanded
) in theOnInitialized
event. - Uses the private field to maintain its internal toggle state, which demonstrates how to avoid writing directly to a parameter.
Note
The advice in this section extends to similar logic in component parameter set
accessors, which can result in similar undesirable side effects.
Shared/Expander.razor
:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
<div class="card-body">
<h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>
@if (expanded)
{
<p class="card-text">@ChildContent</p>
}
</div>
</div>
@code {
private bool expanded;
[Parameter]
public bool Expanded { private get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
protected override void OnInitialized()
{
expanded = Expanded;
}
private void Toggle()
{
expanded = !expanded;
}
}
For additional information, see Blazor Two Way Binding Error (dotnet/aspnetcore #24599).
For more information on change detection, including information on the exact types that Blazor checks, see ASP.NET Core Razor component rendering.
Attribute splatting and arbitrary parameters
Components can capture and render additional attributes in addition to the component's declared parameters. Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes
Razor directive attribute. This scenario is useful for defining a component that produces a markup element that supports a variety of customizations. For example, it can be tedious to define attributes separately for an <input>
that supports many parameters.
In the following Splat
component:
- The first
<input>
element (id="useIndividualParams"
) uses individual component parameters. - The second
<input>
element (id="useAttributesDict"
) uses attribute splatting.
Pages/Splat.razor
:
@page "/splat"
<input id="useIndividualParams"
maxlength="@maxlength"
placeholder="@placeholder"
required="@required"
size="@size" />
<input id="useAttributesDict"
@attributes="InputAttributes" />
@code {
private string maxlength = "10";
private string placeholder = "Input placeholder text";
private string required = "required";
private string size = "50";
private Dictionary<string, object> InputAttributes { get; set; } =
new Dictionary<string, object>()
{
{ "maxlength", "10" },
{ "placeholder", "Input placeholder text" },
{ "required", "required" },
{ "size", "50" }
};
}
The rendered <input>
elements in the webpage are identical:
<input id="useIndividualParams"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
<input id="useAttributesDict"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50">
To accept arbitrary attributes, define a component parameter with the CaptureUnmatchedValues property set to true
:
@code {
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> InputAttributes { get; set; }
}
The CaptureUnmatchedValues property on [Parameter]
allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with CaptureUnmatchedValues. The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object>
with string keys. Use of IEnumerable<KeyValuePair<string, object>>
or IReadOnlyDictionary<string, object>
are also options in this scenario.
The position of @attributes
relative to the position of element attributes is important. When @attributes
are splatted on the element, the attributes are processed from right to left (last to first). Consider the following example of a parent component that consumes a child component:
Shared/AttributeOrderChild1.razor
:
<div @attributes="AdditionalAttributes" extra="5" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent1.razor
:
@page "/attribute-order-parent-1"
<AttributeOrderChild1 extra="10" />
The AttributeOrderChild1
component's extra
attribute is set to the right of @attributes
. The AttributeOrderParent1
component's rendered <div>
contains extra="5"
when passed through the additional attribute because the attributes are processed right to left (last to first):
<div extra="5" />
In the following example, the order of extra
and @attributes
is reversed in the child component's <div>
:
Shared/AttributeOrderChild2.razor
:
<div extra="5" @attributes="AdditionalAttributes" />
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Pages/AttributeOrderParent2.razor
:
@page "/attribute-order-parent-2"
<AttributeOrderChild2 extra="10" />
The <div>
in the parent component's rendered webpage contains extra="10"
when passed through the additional attribute:
<div extra="10" />
Capture references to components
Component references provide a way to reference a component instance for issuing commands. To capture a component reference:
- Add an
@ref
attribute to the child component. - Define a field with the same type as the child component.
When the component is rendered, the field is populated with the component instance. You can then invoke .NET methods on the instance.
Consider the following ReferenceChild
component that logs a message when its ChildMethod
is called.
Shared/ReferenceChild.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger
@code {
public void ChildMethod(int value)
{
logger.LogInformation("Received {Value} in ChildMethod", value);
}
}
A component reference is only populated after the component is rendered and its output includes ReferenceChild
's element. Until the component is rendered, there's nothing to reference.
To manipulate component references after the component has finished rendering, use the OnAfterRender
or OnAfterRenderAsync
methods.
To use a reference variable with an event handler, use a lambda expression or assign the event handler delegate in the OnAfterRender
or OnAfterRenderAsync
methods. This ensures that the reference variable is assigned before the event handler is assigned.
The following lambda approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent1.razor
:
@page "/reference-parent-1"
<button @onclick="@(() => childComponent.ChildMethod(5))">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild childComponent;
}
The following delegate approach uses the preceding ReferenceChild
component.
Pages/ReferenceParent2.razor
:
@page "/reference-parent-2"
<button @onclick="callChildMethod">
Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>
<ReferenceChild @ref="childComponent" />
@code {
private ReferenceChild childComponent;
private Action callChildMethod;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
callChildMethod = CallChildMethod;
}
}
private void CallChildMethod()
{
childComponent.ChildMethod(5);
}
}
While capturing component references use a similar syntax to capturing element references, capturing component references isn't a JavaScript interop feature. Component references aren't passed to JavaScript code. Component references are only used in .NET code.
Important
Do not use component references to mutate the state of child components. Instead, use normal declarative component parameters to pass data to child components. Use of component parameters result in child components that rerender at the correct times automatically. For more information, see the component parameters section and the ASP.NET Core Blazor data binding article.
Synchronization context
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and event callbacks raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently.
Avoid thread-blocking calls
Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying Task is complete:
Note
Blazor documentation examples that use the thread-blocking methods mentioned in this section are only using the methods for demonstration purposes, not as recommended coding guidance. For example, a few component code demonstrations simulate a long-running process by calling Thread.Sleep.
Invoke component methods externally to update state
In the event a component must be updated based on an external event, such as a timer or other notification, use the InvokeAsync
method, which dispatches code execution to Blazor's synchronization context. For example, consider the following notifier service that can notify any listening component about updated state. The Update
method can be called from anywhere in the app.
TimerService.cs
:
using System;
using System.Timers;
using Microsoft.Extensions.Logging;
public class TimerService : IDisposable
{
private int elapsedCount;
private readonly ILogger<TimerService> logger;
private readonly NotifierService notifier;
private Timer timer;
public TimerService(NotifierService notifier, ILogger<TimerService> logger)
{
this.notifier = notifier;
this.logger = logger;
}
public void Start()
{
if (timer is null)
{
timer = new Timer();
timer.AutoReset = true;
timer.Interval = 10000;
timer.Elapsed += HandleTimer;
timer.Enabled = true;
logger.LogInformation("Started");
}
}
private async void HandleTimer(object source, ElapsedEventArgs e)
{
elapsedCount += 1;
await notifier.Update("elapsedCount", elapsedCount);
logger.LogInformation($"elapsedCount: {elapsedCount}");
}
public void Dispose()
{
timer?.Dispose();
}
}
NotifierService.cs
:
using System;
using System.Threading.Tasks;
public class NotifierService
{
public async Task Update(string key, int value)
{
if (Notify != null)
{
await Notify.Invoke(key, value);
}
}
public event Func<string, int, Task> Notify;
}
Register the services:
In a Blazor WebAssembly app, register the services as singletons in
Program.cs
:builder.Services.AddSingleton<NotifierService>(); builder.Services.AddSingleton<TimerService>();
In a Blazor Server app, register the services as scoped in
Startup.ConfigureServices
:services.AddScoped<NotifierService>(); services.AddScoped<TimerService>();
Use the NotifierService
to update a component.
Pages/ReceiveNotifications.razor
:
@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer
<h1>Receive Notifications</h1>
<h2>Timer Service</h2>
<button @onclick="StartTimer">Start Timer</button>
<h2>Notifications</h2>
<p>
Status:
@if (lastNotification.key != null)
{
<span>@lastNotification.key = @lastNotification.value</span>
}
else
{
<span>Awaiting first notification</span>
}
</p>
@code {
private (string key, int value) lastNotification;
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public async Task OnNotify(string key, int value)
{
await InvokeAsync(() =>
{
lastNotification = (key, value);
StateHasChanged();
});
}
private void StartTimer()
{
Timer.Start();
}
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
In the preceding example:
NotifierService
invokes the component'sOnNotify
method outside of Blazor's synchronization context.InvokeAsync
is used to switch to the correct context and queue a render. For more information, see ASP.NET Core Razor component rendering.- The component implements IDisposable. The
OnNotify
delegate is unsubscribed in theDispose
method, which is called by the framework when the component is disposed. For more information, see ASP.NET Core Razor component lifecycle.
Important
If a Razor component defines an event that's triggered from a background thread, the component might be required to capture and restore the execution context (ExecutionContext) at the time the handler is registered. For more information, see Calling InvokeAsync(StateHasChanged)
causes page to fallback to default culture (dotnet/aspnetcore #28521).
Use @key
to control the preservation of elements and components
When rendering a list of elements or components and the elements or components subsequently change, Blazor must decide which of the previous elements or components are retained and how model objects should map to them. Normally, this process is automatic and sufficient for general rendering, but there are often cases where controlling the process using the @key
directive attribute is required.
Consider the following example that demonstrates a collection mapping problem that's solved by using @key
.
For the following Details
and PeopleExample
components:
- The
Details
component receives data (Data
) from the parentPeopleExample
component, which is displayed in an<input>
element. Any given displayed<input>
element can receive the focus of the page from the user when they select one of the<input>
elements. - The
PeopleExample
component creates a list of person objects for display using theDetails
component. Every three seconds, a new person is added to the collection.
This demonstration allows you to:
- Select an
<input>
from among several renderedDetails
components. - Study the behavior of the page's focus as the people collection automatically grows.
Shared/Details.razor
:
<input value="@Data" />
@code {
[Parameter]
public string Data { get; set; }
}
In the following PeopleExample
component, each iteration of adding a person in OnTimerCallback
results in Blazor rebuilding the entire collection. The page's focus remains on the same index position of <input>
elements, so the focus shifts each time a person is added. Shifting the focus away from what the user selected isn't desirable behavior. After demonstrating the poor behavior with the following component, the @key
directive attribute is used to improve the user's experience.
Pages/PeopleExample.razor
:
@page "/people-example"
@using System.Timers
@implements IDisposable
@foreach (var person in people)
{
<Details Data="@person.Data" />
}
@code {
private Timer timer = new Timer(3000);
public List<Person> people =
new List<Person>()
{
{ new Person { Data = "Person 1" } },
{ new Person { Data = "Person 2" } },
{ new Person { Data = "Person 3" } }
};
protected override void OnInitialized()
{
timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer.Start();
}
private void OnTimerCallback()
{
_ = InvokeAsync(() =>
{
people.Insert(0,
new Person
{
Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
});
StateHasChanged();
});
}
public void Dispose() => timer.Dispose();
public class Person
{
public string Data { get; set; }
}
}
The contents of the people
collection changes with inserted, deleted, or re-ordered entries. Rerendering can lead to visible behavior differences. For example, each time a person is inserted into the people
collection, the user's focus is lost.
The mapping process of elements or components to a collection can be controlled with the @key
directive attribute. Use of @key
guarantees the preservation of elements or components based on the key's value. If the Details
component in the preceding example is keyed on the person
item, Blazor ignores rerendering Details
components that haven't changed.
To modify the PeopleExample
component to use the @key
directive attribute with the people
collection, update the <Details>
element to the following:
<Details @key="person" Data="@person.Data" />
When the people
collection changes, the association between Details
instances and person
instances is retained. When a Person
is inserted at the beginning of the collection, one new Details
instance is inserted at that corresponding position. Other instances are left unchanged. Therefore, the user's focus isn't lost as people are added to the collection.
Other collection updates exhibit the same behavior when the @key
directive attribute is used:
- If an instance is deleted from the collection, only the corresponding component instance is removed from the UI. Other instances are left unchanged.
- If collection entries are re-ordered, the corresponding component instances are preserved and re-ordered in the UI.
Important
Keys are local to each container element or component. Keys aren't compared globally across the document.
When to use @key
Typically, it makes sense to use @key
whenever a list is rendered (for example, in a foreach
block) and a suitable value exists to define the @key
.
You can also use @key
to preserve an element or component subtree when an object doesn't change, as the following examples show.
Example 1:
<li @key="person">
<input value="@person.Data" />
</li>
Example 2:
<div @key="person">
@* other HTML elements *@
</div>
If an person
instance changes, the @key
attribute directive forces Blazor to:
- Discard the entire
<li>
or<div>
and their descendants. - Rebuild the subtree within the UI with new elements and components.
This is useful to guarantee that no UI state is preserved when the collection changes within a subtree.
Scope of @key
The @key
attribute directive is scoped to its own siblings within its parent.
Consider the following example. The first
and second
keys are compared against each other within the same scope of the outer <div>
element:
<div>
<div @key="first">...</div>
<div @key="second">...</div>
</div>
The following example demonstrates first
and second
keys in their own scopes, unrelated to each other and without influence on each other. Each @key
scope only applies to its parent <div>
element, not across the parent <div>
elements:
<div>
<div @key="first">...</div>
</div>
<div>
<div @key="second">...</div>
</div>
For the Details
component shown earlier, the following examples render person
data within the same @key
scope and demonstrate typical use cases for @key
:
<div>
@foreach (var person in people)
{
<Details @key="person" Data="@person.Data" />
}
</div>
@foreach (var person in people)
{
<div @key="person">
<Details Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li @key="person">
<Details Data="@person.Data" />
</li>
}
</ol>
The following examples only scope @key
to the <div>
or <li>
element that surrounds each Details
component instance. Therefore, person
data for each member of the people
collection is not keyed on each person
instance across the rendered Details
components. Avoid the following patterns when using @key
:
@foreach (var person in people)
{
<div>
<Details @key="person" Data="@person.Data" />
</div>
}
<ol>
@foreach (var person in people)
{
<li>
<Details @key="person" Data="@person.Data" />
</li>
}
</ol>
When not to use @key
There's a performance cost when rendering with @key
. The performance cost isn't large, but only specify @key
if preserving the element or component benefits the app.
Even if @key
isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using @key
is control over how model instances are mapped to the preserved component instances, instead of Blazor selecting the mapping.
Values to use for @key
Generally, it makes sense to supply one of the following values for @key
:
- Model object instances. For example, the
Person
instance (person
) was used in the earlier example. This ensures preservation based on object reference equality. - Unique identifiers. For example, unique identifiers can be based on primary key values of type
int
,string
, orGuid
.
Ensure that values used for @key
don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values.
Apply an attribute
Attributes can be applied to components with the @attribute
directive. The following example applies the [Authorize]
attribute to the component's class:
@page "/"
@attribute [Authorize]
Conditional HTML element attributes
HTML element attribute properties are conditionally set based on the .NET value. If the value is false
or null
, the property isn't set. If the value is true
, the property is set.
In the following example, IsCompleted
determines if the <input>
element's checked
property is set.
Pages/ConditionalAttribute.razor
:
@page "/conditional-attribute"
<label>
<input type="checkbox" checked="@IsCompleted" />
Is Completed?
</label>
<button @onclick="@(() => IsCompleted = !IsCompleted)">
Change IsCompleted
</button>
@code {
[Parameter]
public bool IsCompleted { get; set; }
}
For more information, see Razor syntax reference for ASP.NET Core.
Warning
Some HTML attributes, such as aria-pressed
, don't function properly when the .NET type is a bool
. In those cases, use a string
type instead of a bool
.
Raw HTML
Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. To render raw HTML, wrap the HTML content in a MarkupString value. The value is parsed as HTML or SVG and inserted into the DOM.
Warning
Rendering raw HTML constructed from any untrusted source is a security risk and should always be avoided.
The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component.
Pages/MarkupStringExample.razor
:
@page "/markup-string-example"
@((MarkupString)myMarkup)
@code {
private string myMarkup =
"<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}
Razor templates
Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format:
@<{HTML tag}>...</{HTML tag}>
The following example illustrates how to specify RenderFragment and RenderFragment<TValue> values and render templates directly in a component. Render fragments can also be passed as arguments to templated components.
Pages/RazorTemplate.razor
:
@page "/razor-template"
@timeTemplate
@petTemplate(new Pet { Name = "Nutty Rex" })
@code {
private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;
private class Pet
{
public string Name { get; set; }
}
}
Rendered output of the preceding code:
<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>
Static assets
Blazor follows the convention of ASP.NET Core apps for static assets. Static assets are located in the project's web root
(wwwroot
) folder or folders under the wwwroot
folder.
Use a base-relative path (/
) to refer to the web root for a static asset. In the following example, logo.png
is physically located in the {PROJECT ROOT}/wwwroot/images
folder. {PROJECT ROOT}
is the app's project root.
<img alt="Company logo" src="/images/logo.png" />
Components do not support tilde-slash notation (~/
).
For information on setting an app's base path, see Host and deploy ASP.NET Core Blazor.
Tag Helpers aren't supported in components
Tag Helpers
aren't supported in components. To provide Tag Helper-like functionality in Blazor, create a component with the same functionality as the Tag Helper and use the component instead.
Scalable Vector Graphics (SVG) images
Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg
), are supported via the <img>
tag:
<img alt="Example image" src="image.svg" />
Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css
):
.element-class {
background-image: url("image.svg");
}
Whitespace rendering behavior
Whitespace is retained in a component's source markup. Whitespace-only text renders in the browser's DOM even when there's no visual effect.
Consider the following component markup:
<ul>
@foreach (var item in Items)
{
<li>
@item.Text
</li>
}
</ul>
The preceding example renders the following unnecessary whitespace:
- Outside of the
@foreach
code block. - Around the
<li>
element. - Around the
@item.Text
output.
A list of 100 items results in over 400 areas of whitespace. None of the extra whitespace visually affects the rendered output.
When rendering static HTML for components, whitespace inside a tag isn't preserved. For example, view the rendered output of the following <img>
tag in a component Razor file (.razor
):
<img alt="Example image" src="img.png" />
Whitespace isn't preserved from the preceding markup:
<img alt="Example image" src="img.png" />
Generic type parameter support
The @typeparam
directive declares a generic type parameter for the generated component class:
@typeparam TItem
In the following example, the ListGenericTypeItems1
component is generically typed as TExample
.
Shared/ListGenericTypeItems1.razor
:
@typeparam TExample
@if (ExampleList != null)
{
<ul>
@foreach (var item in ExampleList)
{
<li>@item</li>
}
</ul>
}
@code {
[Parameter]
public IEnumerable<TExample> ExampleList{ get; set; }
}
The following GenericTypeExample1
component renders two ListGenericTypeItems1
components:
- String or integer data is assigned to the
ExampleList
parameter of each component. - Type
string
orint
that matches the type of the assigned data is set for the type parameter (TExample
) of each component.
Pages/GenericTypeExample1.razor
:
@page "/generic-type-example-1"
<h1>Generic Type Example 1</h1>
<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })"
TExample="string" />
<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })"
TExample="int" />
For more information, see the following articles:
Feedback
Submit and view feedback for