From 489bb6363fa8e6ff4365f3d03aaed9a294bd0cb6 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 9 May 2023 09:14:49 -0400 Subject: [PATCH] Components overview versioning (#29206) --- aspnetcore/blazor/components/index.md | 5078 ++++++------------------- 1 file changed, 1235 insertions(+), 3843 deletions(-) diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index db91bef05d..8570f2f796 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -14,8 +14,6 @@ uid: blazor/components/index This article explains how to create and use Razor components in Blazor apps, including guidance on Razor syntax, component naming, namespaces, and component parameters. -:::moniker range=">= aspnetcore-7.0" - 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](xref:blazor/components/prerendering-and-integration). ## Component classes @@ -61,8 +59,30 @@ The following `HelloWorld` component uses a route template of `/hello-world`, an `Pages/HelloWorld.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: + +:::moniker-end + 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 . @@ -84,8 +104,30 @@ Component members are used in rendering logic using C# expressions that start wi `Pages/Markup.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: + +:::moniker-end + > [!NOTE] > Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) 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)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). @@ -105,14 +147,58 @@ Consider the following `Heading` component, which can be used by other component `Shared/Heading.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: + +:::moniker-end + The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `` tag appears. `Pages/HeadingExample.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: + +:::moniker-end + 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`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Class name and namespace](#class-name-and-namespace) section. The `Heading` component example shown in this section doesn't have an [`@page`][9] 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`][9] 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`. @@ -162,23 +248,73 @@ Components are generated as [C# partial classes](/dotnet/csharp/programming-guid * A single file contains C# code defined in one or more [`@code`][1] 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`). +:::moniker range=">= aspnetcore-5.0" + > [!NOTE] > A component stylesheet that defines component-specific styles is a separate file (`.css`). Blazor CSS isolation is described later in . +:::moniker-end + The following example shows the default `Counter` component with an [`@code`][1] 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`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/Counter.razor"::: + +:::moniker-end + The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: `Pages/CounterPartialClass.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: + +:::moniker-end + `Pages/CounterPartialClass.razor.cs`: +:::moniker range=">= aspnetcore-6.0" + ```csharp namespace BlazorSample.Pages; @@ -193,10 +329,33 @@ public partial class CounterPartialClass } ``` +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +```csharp +namespace BlazorSample.Pages +{ + public partial class CounterPartialClass + { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } + } +} +``` + +:::moniker-end + [`@using`][2] 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: +:::moniker range=">= aspnetcore-6.0" + ```csharp using System.Net.Http; using Microsoft.AspNetCore.Authorization; @@ -208,6 +367,20 @@ using Microsoft.AspNetCore.Components.Web.Virtualization; using Microsoft.JSInterop; ``` +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +```csharp +using System.Net.Http; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.JSInterop; +``` + +:::moniker-end + Typical namespaces also include the namespace of the app and the namespace corresponding to the app's `Shared` folder: ```csharp @@ -221,24 +394,112 @@ The [`@inherits`][6] directive is used to specify a base class for a component. `Pages/BlazorRocks.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: + +:::moniker-end + `BlazorRocksBase.cs`: +:::moniker range=">= aspnetcore-7.0" + :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: + +:::moniker-end + ## Component parameters *Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type () and a user-defined reference type (`PanelBody`) are passed as component parameters. `PanelBody.cs`: +:::moniker range=">= aspnetcore-7.0" + :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/PanelBody.cs"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/PanelBody.cs"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/PanelBody.cs"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/PanelBody.cs"::: + +:::moniker-end + `Shared/ParameterChild.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: + +:::moniker-end + > [!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](#overwritten-parameters) section of this article. @@ -249,8 +510,30 @@ The `Title` and `Body` component parameters of the `ParameterChild` component ar `Pages/ParameterParent.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: + +:::moniker-end + 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] @@ -295,8 +578,30 @@ Throughout the documentation, code examples: `Pages/ParameterParent2.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: + +:::moniker-end + > [!NOTE] > When assigning a C# member to a component parameter, don't prefix the parameter's HTML attribute with `@`. > @@ -361,8 +666,30 @@ To support the assignment of a composed value, use a method, field, or property. `Pages/ParameterParent3.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: + +:::moniker-end + For more information, see . > [!WARNING] @@ -393,6 +720,8 @@ public DateTime StartData { get; set; } = DateTime.Now; After the initial assignment of , do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. +:::moniker range=">= aspnetcore-6.0" + Apply the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) 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](xref:Microsoft.AspNetCore.Components.ParameterAttribute). The 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. ```csharp @@ -448,16 +777,50 @@ Only ***unnamed tuples*** are supported for C# 7.0 or later in Razor components. Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https://www.uphe.com/movies/serenity-2005) ([Nathan Fillion](https://www.imdb.com/name/nm0277213/)) +:::moniker-end + ## Route parameters Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. +:::moniker range=">= aspnetcore-5.0" + 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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +Optional route parameters aren't supported, so two [`@page`][9] directives are applied in the following example. The first [`@page`][9] directive permits navigation to the component without a route parameter. The second [`@page`][9] directive receives the `{text}` route parameter and assigns the value to the `Text` property. + +:::moniker-end + `Pages/RouteParameter.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="2,7-8"::: + +:::moniker-end + For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see . ## Child content render fragments @@ -468,8 +831,30 @@ In the following example, the `RenderFragmentChild` component has a `ChildConten `Shared/RenderFragmentChild.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: + +:::moniker-end + > [!IMPORTANT] > The property receiving the content must be named `ChildContent` by convention. > @@ -479,8 +864,30 @@ The following `RenderFragmentParent` component provides content for rendering th `Pages/RenderFragmentParent.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: + +:::moniker-end + Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/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: ```razor @@ -569,8 +976,30 @@ After the following `Expander` component demonstrates an overwritten parameter, `Shared/Expander.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: + +:::moniker-end + The `Expander` component is added to the following `ExpanderExample` parent component that may call : * Calling in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. is covered in more detail later in and . @@ -578,8 +1007,30 @@ The `Expander` component is added to the following `ExpanderExample` parent comp `Pages/ExpanderExample.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: + +:::moniker-end + Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. If is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed: @@ -605,10 +1056,42 @@ The following revised `Expander` component: `Shared/Expander.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0" + For two-way parent-child binding examples, see . For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). + +:::moniker-end + For more information on change detection, including information on the exact types that Blazor checks, see . ## Attribute splatting and arbitrary parameters @@ -622,8 +1105,30 @@ In the following `Splat` component: `Pages/Splat.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: + +:::moniker-end + The rendered `` elements in the webpage are identical: ```html @@ -655,12 +1160,56 @@ The position of [`@attributes`][3] relative to the position of element attribute `Shared/AttributeOrderChild1.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: + +:::moniker-end + `Pages/AttributeOrderParent1.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: + +:::moniker-end + The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `
` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): ```html @@ -671,12 +1220,56 @@ In the following example, the order of `extra` and [`@attributes`][3] is reverse `Shared/AttributeOrderChild2.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: + +:::moniker-end + `Pages/AttributeOrderParent2.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: + +:::moniker-end + The `
` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: ```html @@ -696,8 +1289,30 @@ Consider the following `ReferenceChild` component that logs a message when its ` `Shared/ReferenceChild.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: + +:::moniker-end + 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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). @@ -708,14 +1323,58 @@ The following lambda approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent1.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: + +:::moniker-end + The following delegate approach uses the preceding `ReferenceChild` component. `Pages/ReferenceParent2.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: + +:::moniker-end + While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), 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] @@ -747,12 +1406,56 @@ In the event a component must be updated based on an external event, such as a t `TimerService.cs`: +:::moniker range=">= aspnetcore-7.0" + :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/TimerService.cs"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/TimerService.cs"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/TimerService.cs"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/TimerService.cs"::: + +:::moniker-end + `NotifierService.cs`: +:::moniker range=">= aspnetcore-7.0" + :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/NotifierService.cs"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/NotifierService.cs"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/NotifierService.cs"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/NotifierService.cs"::: + +:::moniker-end + Register the services: * In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: @@ -773,8 +1476,30 @@ Use the `NotifierService` to update a component. `Pages/ReceiveNotifications.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: + +:::moniker-end + In the preceding example: * `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see . @@ -783,16 +1508,12 @@ In the preceding example: > [!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 () at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/28521). -:::moniker-end - :::moniker range=">= aspnetcore-8.0" To dispatch caught exceptions from the background `TimerService` to the component to treat the exceptions like normal lifecycle event exceptions, see [Handle caught exceptions outside of a Razor component's lifecycle](xref:blazor/fundamentals/handle-errors#handle-caught-exceptions-outside-of-a-razor-components-lifecycle) in the *Handle errors* article. :::moniker-end -:::moniker range=">= aspnetcore-7.0" - ## 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`][5] directive attribute is required. @@ -811,1476 +1532,58 @@ This demonstration allows you to: `Shared/Details.razor`: +:::moniker range=">= aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: -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 `` 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`][5] directive attribute is used to improve the user's experience. - -`Pages/PeopleExample.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: - -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`][5] directive attribute. Use of [`@key`][5] 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`][5] directive attribute with the `people` collection, update the `
` element to the following: - -```razor -
-``` - -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`][5] 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`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. - -You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. - -Example 1: - -```razor -
  • - -
  • -``` - -Example 2: - -```razor -
    - @* other HTML elements *@ -
    -``` - -If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: - -* Discard the entire `
  • ` or `
    ` 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`][5] 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 `
    ` element: - -```razor -
    -
    ...
    -
    ...
    -
    -``` - -The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `
    ` element, not across the parent `
    ` elements: - -```razor -
    -
    ...
    -
    -
    -
    ...
    -
    -``` - -For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: - -```razor -
    - @foreach (var person in people) - { -
    - } -
    -``` - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -The following examples only scope [`@key`][5] to the `
    ` or `
  • ` 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`][5]: - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -### When not to use `@key` - -There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. - -Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] 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`][5]: - -* 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`, or `Guid`. - -Ensure that values used for [`@key`][5] 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`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: - -```razor -@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 `` element's `checked` property is set. - -`Pages/ConditionalAttribute.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: - -For more information, see . - -> [!WARNING] -> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), 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 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 type to add a block of static HTML content to the rendered output of a component. - -`Pages/MarkupStringExample.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: - -## Razor templates - -Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: - -```razor -@<{HTML tag}>... -``` - -The following example illustrates how to specify and values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). - -`Pages/RazorTemplate.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: - -Rendered output of the preceding code: - -```html -

    The time is 4/19/2021 8:54:46 AM.

    -

    Pet: Nutty Rex

    -``` - -## 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](xref:fundamentals/index#web-root) 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. - -```razor -Company logo -``` - -Components do **not** support tilde-slash notation (`~/`). - -For information on setting an app's base path, see . - -## Tag Helpers aren't supported in components - -[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) 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`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `` tag: - -```html -Example image -``` - -Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): - -```css -.element-class { - background-image: url("image.svg"); -} -``` - -Blazor supports the [``](https://developer.mozilla.org/docs/Web/SVG/Element/foreignObject) element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a , or a Razor component. - -The following example demonstrates: - -* Display of a `string` (`@message`). -* Two-way binding with an `` element and a `value` field. -* A `Robot` component. - -```razor - - - -

    @message

    -
    -
    - - - - - - - - - - - - - -@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`](xref:mvc/views/razor#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 / 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`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: - -```razor -@typeparam TItem -``` - -C# syntax with [`where`](/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) type constraints is supported: - -```razor -@typeparam TEntity where TEntity : IEntity -``` - -In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. - -`Shared/ListGenericTypeItems1.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: - -The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: - -* String or integer data is assigned to the `ExampleList` parameter of each component. -* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. - -`Pages/GenericTypeExample1.razor`: - -:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: - -For more information, see . For an example of generic typing with templated components, see . - -## Cascaded generic type support - -An ancestor component can cascade a type parameter by name to descendants using the [`[CascadingTypeParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute). 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`](xref:mvc/views/razor#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 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`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [Parameter] - public IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListDisplay2.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [Parameter] - public IEnumerable? 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](#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`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 2

    - -@ChildContent - -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } -} -``` - -The following `GenericTypeExample2` parent component sets the child content () 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`: - -```razor -@page "/generic-type-example-2" - -

    Generic Type Example 2

    - - - - - - - - - - -``` - -Specifying the type explicitly also allows the use of [cascading values and parameters](xref:blazor/components/cascading-values-and-parameters) to provide data to child components, as the following demonstration shows. - -`Shared/ListDisplay3.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [CascadingParameter] - protected IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListDisplay4.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [CascadingParameter] - protected IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListGenericTypeItems3.razor`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 3

    - -@ChildContent - -@if (ExampleList is not null) -{ -
      - @foreach(var item in ExampleList) - { -
    • @item
    • - } -
    - -

    - Type of TExample: @typeof(TExample) -

    -} - -@code { - [CascadingParameter] - protected IEnumerable? 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`: - -```razor -@page "/generic-type-example-3" - -

    Generic Type Example 3

    - - - - - - - - - - - - - - - -@code { - private List stringData = new() { "Item 1", "Item 2" }; - private List 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: - -```razor - -``` - -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: - -```razor - -``` - -### 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](#cascaded-generic-type-support) section. - -`Shared/ListGenericTypeItems4.razor`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 4

    - -@ChildContent - -@if (ExampleList is not null) -{ -
      - @foreach(var item in ExampleList) - { -
    • @item
    • - } -
    - -

    - Type of TExample: @typeof(TExample) -

    -} - -@code { - [Parameter] - public IEnumerable? 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`: - -```razor -@page "/generic-type-example-4" - -

    Generic Type Example 4

    - - - - - - - - - - -``` - -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`: - -```razor -@page "/generic-type-example-5" - -

    Generic Type Example 5

    - - - - - - - - - - - -@code { - private List stringData = new() { "Item 1", "Item 2" }; - private List 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](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper): - -```cshtml - -``` - -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`: - -```csharp -builder.RootComponents.Add("#app"); -``` - -In the preceding code, the CSS selector, `#app`, indicates that the `App` component is created for the `
    ` in `wwwroot/index.html` with an `id` of `app`: - -```html -
    ... -``` - -MVC and Razor Pages apps can also use the [Component Tag Helper](xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper) to register statically-rendered Blazor WebAssembly root components: - -```cshtml - -``` - -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: - -* -* - :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" -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](xref:blazor/components/prerendering-and-integration). - -## Component classes - -Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. - -By default, is the base class for components described by Razor component files. implements the lowest abstraction of components, the interface. 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](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): 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. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -Developers typically create Razor components from Razor component files (`.razor`) or base their components on , but components can also be built by implementing . Developer-built components that implement 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](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: - -* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] 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](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `` 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 . - -### 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 the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` 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`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a specifying the route template. At runtime, the router searches for component classes with a 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: - -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 . - -### Markup - -A component's UI is defined using [Razor syntax](xref:mvc/views/razor), 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`][1] blocks. In [`@code`][1] 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 value `font-style` of the heading element. -* `headingText` for the content of the heading element. - -`Pages/Markup.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: - -> [!NOTE] -> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) 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)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). - -The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). 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 . - -Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for ). - -### 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 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: - -The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `` tag appears. - -`Pages/HeadingExample.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: - -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`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Class name and namespace](#class-name-and-namespace) section. - -The `Heading` component example shown in this section doesn't have an [`@page`][9] 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`][9] 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](/dotnet/csharp/programming-guide/classes-and-structs/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 is `BlazorSample.Pages`. -* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. - -For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: - -```razor -@using BlazorSample.Components -``` - -> [!NOTE] -> [`@using`][2] 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`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: - -```razor - -``` - -The namespace of a component authored with Razor is based on the following (in priority order): - -* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). -* The project's `RootNamespace` in the project file (for example, `BlazorSample`). -* 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 of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` 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. - -The following are **not** supported: - -* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. -* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) 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 the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with ``. - -### Partial class support - -Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: - -* A single file contains C# code defined in one or more [`@code`][1] 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 . - -The following example shows the default `Counter` component with an [`@code`][1] 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: - -The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: - -`Pages/CounterPartialClass.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/CounterPartialClass.razor"::: - -`Pages/CounterPartialClass.razor.cs`: - -```csharp -namespace BlazorSample.Pages; - -public partial class CounterPartialClass -{ - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} -``` - -[`@using`][2] 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: - -```csharp -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: - -```csharp -using BlazorSample; -using BlazorSample.Shared; -``` - -### Specify a base class - -The [`@inherits`][6] 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 . - -`Pages/BlazorRocks.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: - -`BlazorRocksBase.cs`: - -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: - -## Component parameters - -*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type () and a user-defined reference type (`PanelBody`) are passed as component parameters. - -`PanelBody.cs`: - -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/PanelBody.cs"::: - -`Shared/ParameterChild.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: - -> [!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](#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 for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. - -`Pages/ParameterParent.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: - -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. - -```html -

    Child component (without attribute values)

    - -
    -
    Set By Child
    -
    Set by child.
    -
    - -

    Child component (with attribute values)

    - -
    -
    Set by Parent
    -
    Set by parent.
    -
    -``` - -Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value. The value of the attribute can typically be any C# expression that matches the type of the parameter. The value of the attribute can optionally lead with a [Razor reserved `@` symbol](xref:mvc/views/razor#razor-syntax), but it isn't required. - -If the component parameter is of type string, then the attribute value is instead treated as a C# string literal by default. If you want to specify a C# expression instead, then use the `@` prefix. - -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 , which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). -* The `panelData` object's `Title` property. - -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"`. -* Use the `@` prefix with nonliterals, ***even when it's optional***. Example: `Count="@ct"`, where `ct` is a number-typed variable. `Count="ct"` is a valid stylistic approach, but the documentation and examples don't adopt the convention. -* Always avoid `@` for literals, outside of Razor expressions. Example: `IsFixed="true"`. - -`Pages/ParameterParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: - -> [!NOTE] -> When assigning a C# member to a component parameter, don't prefix the parameter's HTML attribute with `@`. -> -> Correct (`Title` is a string parameter, `Count` is a number-typed parameter): -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` -> -> Incorrect: -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` - -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](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: - -```razor - -``` - -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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: - -```razor - - -@code { - private string? title; - - protected override async Task OnInitializedAsync() - { - title = await ...; - } -} -``` - -For more information, see . - -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: - -```razor - -``` - -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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: - -For more information, see . - -> [!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](#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: - -```csharp -[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`](xref:blazor/components/lifecycle#after-parameters-are-set-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 with to `StartData` is valid syntax in a component: - -```csharp -[Parameter] -public DateTime StartData { get; set; } = DateTime.Now; -``` - -After the initial assignment of , do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. - -Apply the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) 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](xref:Microsoft.AspNetCore.Components.ParameterAttribute). The 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. - -```csharp -[Parameter] -[EditorRequired] -public string? Title { get; set; } -``` - -Single-line attribute lists are also supported: - -```csharp -[Parameter, EditorRequired] -public string? Title { get; set; } -``` - -[`Tuples`](/dotnet/csharp/language-reference/builtin-types/value-tuples) ([API documentation](xref:System.Tuple)) are supported for component parameters and [`RenderFragment`](#child-content-render-fragments) types. The following component parameter example passes three values in a `Tuple`: - -`Shared/RenderTupleChild.razor`: - -```csharp -
    -
    Tuple Card
    -
    -
      -
    • Integer: @Data?.Item1
    • -
    • String: @Data?.Item2
    • -
    • Boolean: @Data?.Item3
    • -
    -
    -
    - -@code { - [Parameter] - public Tuple? Data { get; set; } -} -``` - -`Pages/RenderTupleParent.razor`: - -```csharp -@page "/render-tuple-parent" - -

    Render Tuple Parent

    - - - -@code { - private Tuple data = new(999, "I aim to misbehave.", true); -} -``` - -Only ***unnamed tuples*** are supported for C# 7.0 or later in Razor components. [Named tuples](/dotnet/csharp/language-reference/builtin-types/value-tuples#tuple-field-names) 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)](https://github.com/dotnet/aspnetcore/issues/28982). - -Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https://www.uphe.com/movies/serenity-2005) ([Nathan Fillion](https://www.imdb.com/name/nm0277213/)) - -## Route parameters - -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) 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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). - -`Pages/RouteParameter.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: - -For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see . - -## 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 . The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. - -`Shared/RenderFragmentChild.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: - -> [!IMPORTANT] -> The property receiving the content must be named `ChildContent` by convention. -> -> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for . - -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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: - -Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/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: - -```razor -

    Three children with an index variable

    - -@for (int c = 0; c < 3; c++) -{ - var current = c; - - - Count: @current - -} -``` - -Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: - -```razor -

    Second example of three children with an index variable

    - -@foreach (var c in Enumerable.Range(0,3)) -{ - - Count: @c - -} -``` - -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](xref:blazor/components/layouts) -* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) -* [Templated components](xref:blazor/components/templated-components) -* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) - -> [!NOTE] -> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-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")](/dotnet/api/?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 and render the fragment from any location as many times as needed: - -```razor -

    Hello, world!

    - -@RenderWelcomeInfo - -

    Render the welcome info a second time:

    - -@RenderWelcomeInfo - -@code { - private RenderFragment RenderWelcomeInfo = __builder => - { -

    Welcome to your new app!

    - }; -} -``` - -For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). - -## 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: - -The `Expander` component is added to the following `ExpanderExample` parent component that may call : - -* Calling in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. is covered in more detail later in and . -* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in . - -`Pages/ExpanderExample.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: - -Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. - -If 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 , 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 , so a call to in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. -* The second `Expander` component doesn't set child content. Therefore, a potentially mutable doesn't exist. A call to in the parent component doesn't automatically rerender the child component, so the component's `Expanded` 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 the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: - -For two-way parent-child binding examples, see . For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). - -For more information on change detection, including information on the exact types that Blazor checks, see . - -## 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`][3] 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 `` that supports many parameters. - -In the following `Splat` component: - -* The first `` element (`id="useIndividualParams"`) uses individual component parameters. -* The second `` element (`id="useAttributesDict"`) uses attribute splatting. - -`Pages/Splat.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: - -The rendered `` elements in the webpage are identical: - -```html - - - -``` - -To accept arbitrary attributes, define a [component parameter](#component-parameters) with the property set to `true`: - -```razor -@code { - [Parameter(CaptureUnmatchedValues = true)] - public Dictionary? InputAttributes { get; set; } -} -``` - -The property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with . The property type used with must be assignable from [`Dictionary`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. - -The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: - -`Pages/AttributeOrderParent1.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: - -The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `
    ` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): - -```html -
    -``` - -In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `
    `: - -`Shared/AttributeOrderChild2.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: - -`Pages/AttributeOrderParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: - -The `
    ` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: - -```html -
    -``` - -## 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`][4] 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). 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`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: - -The following delegate approach uses the preceding `ReferenceChild` component. - -`Pages/ReferenceParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: - -While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), 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](#component-parameters) section and the article. - -## Synchronization context - -Blazor uses a synchronization context () to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) 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 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 . - -### 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`: - -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/TimerService.cs"::: - -`NotifierService.cs`: - -:::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/NotifierService.cs"::: - -Register the services: - -* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: - - ```csharp - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - ``` - -* In a Blazor Server app, register the services as scoped in `Program.cs`: - - ```csharp - builder.Services.AddScoped(); - builder.Services.AddScoped(); - ``` - -Use the `NotifierService` to update a component. - -`Pages/ReceiveNotifications.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: - -In the preceding example: - -* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see . -* The component implements . The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see . - -> [!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 () at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/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`][5] directive attribute is required. - -Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. - -For the following `Details` and `PeopleExample` components: - -* The `Details` component receives data (`Data`) from the parent `PeopleExample` component, which is displayed in an `` element. Any given displayed `` element can receive the focus of the page from the user when they select one of the `` elements. -* The `PeopleExample` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. - -This demonstration allows you to: - -* Select an `` from among several rendered `Details` components. -* Study the behavior of the page's focus as the people collection automatically grows. - -`Shared/Details.razor`: - :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Details.razor"::: + +:::moniker-end + 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 `` 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`][5] directive attribute is used to improve the user's experience. `Pages/PeopleExample.razor`: +:::moniker range=">= aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: + +:::moniker-end + 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`][5] directive attribute. Use of [`@key`][5] 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. @@ -2439,8 +1742,30 @@ In the following example, `IsCompleted` determines if the `` element's `c `Pages/ConditionalAttribute.razor`: +:::moniker range=">= aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + +:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: + +:::moniker-end + For more information, see . > [!WARNING] @@ -2457,8 +1782,30 @@ The following example shows using the `](https://developer.mozilla.org/docs/Web/SVG/Element/foreignObject) element to display arbitrary HTML within an SVG. The markup can represent arbitrary HTML, a , or a Razor component. The following example demonstrates: @@ -2554,1384 +1925,12 @@ The following example demonstrates: } ``` -## Whitespace rendering behavior - -Unless the [`@preservewhitespace`](xref:mvc/views/razor#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 / 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`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: - -```razor -@typeparam TItem -``` - -C# syntax with [`where`](/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) type constraints is supported: - -```razor -@typeparam TEntity where TEntity : IEntity -``` - -In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. - -`Shared/ListGenericTypeItems1.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: - -The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: - -* String or integer data is assigned to the `ExampleList` parameter of each component. -* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. - -`Pages/GenericTypeExample1.razor`: - -:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: - -For more information, see . For an example of generic typing with templated components, see . - -## Cascaded generic type support - -An ancestor component can cascade a type parameter by name to descendants using the [`[CascadingTypeParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute). 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`](xref:mvc/views/razor#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 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`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [Parameter] - public IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListDisplay2.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [Parameter] - public IEnumerable? 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](#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`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 2

    - -@ChildContent - -@code { - [Parameter] - public RenderFragment? ChildContent { get; set; } -} -``` - -The following `GenericTypeExample2` parent component sets the child content () 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`: - -```razor -@page "/generic-type-example-2" - -

    Generic Type Example 2

    - - - - - - - - - - -``` - -Specifying the type explicitly also allows the use of [cascading values and parameters](xref:blazor/components/cascading-values-and-parameters) to provide data to child components, as the following demonstration shows. - -`Shared/ListDisplay3.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [CascadingParameter] - protected IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListDisplay4.razor`: - -```razor -@typeparam TExample - -@if (ExampleList is not null) -{ -
      - @foreach (var item in ExampleList) - { -
    • @item
    • - } -
    -} - -@code { - [CascadingParameter] - protected IEnumerable? ExampleList { get; set; } -} -``` - -`Shared/ListGenericTypeItems3.razor`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 3

    - -@ChildContent - -@if (ExampleList is not null) -{ -
      - @foreach(var item in ExampleList) - { -
    • @item
    • - } -
    - -

    - Type of TExample: @typeof(TExample) -

    -} - -@code { - [CascadingParameter] - protected IEnumerable? 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`: - -```razor -@page "/generic-type-example-3" - -

    Generic Type Example 3

    - - - - - - - - - - - - - - - -@code { - private List stringData = new() { "Item 1", "Item 2" }; - private List 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: - -```razor - -``` - -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: - -```razor - -``` - -### 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](#cascaded-generic-type-support) section. - -`Shared/ListGenericTypeItems4.razor`: - -```razor -@attribute [CascadingTypeParameter(nameof(TExample))] -@typeparam TExample - -

    List Generic Type Items 4

    - -@ChildContent - -@if (ExampleList is not null) -{ -
      - @foreach(var item in ExampleList) - { -
    • @item
    • - } -
    - -

    - Type of TExample: @typeof(TExample) -

    -} - -@code { - [Parameter] - public IEnumerable? 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`: - -```razor -@page "/generic-type-example-4" - -

    Generic Type Example 4

    - - - - - - - - - - -``` - -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`: - -```razor -@page "/generic-type-example-5" - -

    Generic Type Example 5

    - - - - - - - - - - - -@code { - private List stringData = new() { "Item 1", "Item 2" }; - private List 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](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper): - -```cshtml - -``` - -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`: - -```csharp -builder.RootComponents.Add("#app"); -``` - -In the preceding code, the CSS selector, `#app`, indicates that the `App` component is created for the `
    ` in `wwwroot/index.html` with an `id` of `app`: - -```html -
    ... -``` - -MVC and Razor Pages apps can also use the [Component Tag Helper](xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper) to register statically-rendered Blazor WebAssembly root components: - -```cshtml - -``` - -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: - -* -* - :::moniker-end -:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" - -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](xref:blazor/components/prerendering-and-integration). - -## Component classes - -Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. - -By default, is the base class for components described by Razor component files. implements the lowest abstraction of components, the interface. 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](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): 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. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -Developers typically create Razor components from Razor component files (`.razor`) or base their components on , but components can also be built by implementing . Developer-built components that implement 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](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: - -* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] 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](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `` 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 . - -### 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 the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` 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`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a specifying the route template. At runtime, the router searches for component classes with a 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: - -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 . - -### Markup - -A component's UI is defined using [Razor syntax](xref:mvc/views/razor), 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`][1] blocks. In [`@code`][1] 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 value `font-style` of the heading element. -* `headingText` for the content of the heading element. - -`Pages/Markup.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: - -> [!NOTE] -> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) 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)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). - -The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). 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 . - -Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for ). - -### 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 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: - -The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `` tag appears. - -`Pages/HeadingExample.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: - -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`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Class name and namespace](#class-name-and-namespace) section. - -The `Heading` component example shown in this section doesn't have an [`@page`][9] 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`][9] 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](/dotnet/csharp/programming-guide/classes-and-structs/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 is `BlazorSample.Pages`. -* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. - -For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: - -```razor -@using BlazorSample.Components -``` - -> [!NOTE] -> [`@using`][2] 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`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: - -```razor - -``` - -The namespace of a component authored with Razor is based on the following (in priority order): - -* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). -* The project's `RootNamespace` in the project file (for example, `BlazorSample`). -* 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 of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` 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. - -The following are **not** supported: - -* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. -* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) 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 the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with ``. - -### Partial class support - -Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: - -* A single file contains C# code defined in one or more [`@code`][1] 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 . - -The following example shows the default `Counter` component with an [`@code`][1] 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/Counter.razor"::: - -The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: - -`Pages/CounterPartialClass.razor`: - -```razor -@page "/counter-partial-class" - -

    Counter

    - -

    Current count: @currentCount

    - - -``` - -`Pages/CounterPartialClass.razor.cs`: - -```csharp -namespace BlazorSample.Pages -{ - public partial class CounterPartialClass - { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } - } -} -``` - -[`@using`][2] 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: - -```csharp -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: - -```csharp -using BlazorSample; -using BlazorSample.Shared; -``` - -### Specify a base class - -The [`@inherits`][6] 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 . - -`Pages/BlazorRocks.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: - -`BlazorRocksBase.cs`: - -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: - -## Component parameters - -*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type () and a user-defined reference type (`PanelBody`) are passed as component parameters. - -`PanelBody.cs`: - -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/PanelBody.cs"::: - -`Shared/ParameterChild.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: - -> [!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](#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 for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. - -`Pages/ParameterParent.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: - -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. - -```html -

    Child component (without attribute values)

    - -
    -
    Set By Child
    -
    Set by child.
    -
    - -

    Child component (with attribute values)

    - -
    -
    Set by Parent
    -
    Set by parent.
    -
    -``` - -Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value. The value of the attribute can typically be any C# expression that matches the type of the parameter. The value of the attribute can optionally lead with a [Razor reserved `@` symbol](xref:mvc/views/razor#razor-syntax), but it isn't required. - -If the component parameter is of type string, then the attribute value is instead treated as a C# string literal by default. If you want to specify a C# expression instead, then use the `@` prefix. - -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 , which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). -* The `panelData` object's `Title` property. - -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"`. -* Use the `@` prefix with nonliterals, ***even when it's optional***. Example: `Count="@ct"`, where `ct` is a number-typed variable. `Count="ct"` is a valid stylistic approach, but the documentation and examples don't adopt the convention. -* Always avoid `@` for literals, outside of Razor expressions. Example: `IsFixed="true"`. - -`Pages/ParameterParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: - -> [!NOTE] -> When assigning a C# member to a component parameter, don't prefix the parameter's HTML attribute with `@`. -> -> Correct (`Title` is a string parameter, `Count` is a number-typed parameter): -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` -> -> Incorrect: -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` - -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](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: - -```razor - -``` - -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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: - -```razor - - -@code { - private string title; - - protected override async Task OnInitializedAsync() - { - title = await ...; - } -} -``` - -For more information, see . - -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: - -```razor - -``` - -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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: - -For more information, see . - -> [!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](#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: - -```csharp -[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`](xref:blazor/components/lifecycle#after-parameters-are-set-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 with to `StartData` is valid syntax in a component: - -```csharp -[Parameter] -public DateTime StartData { get; set; } = DateTime.Now; -``` - -After the initial assignment of , do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. - -## Route parameters - -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) 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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). - -`Pages/RouteParameter.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="1,6-7"::: - -For information on catch-all route parameters (`{*pageRoute}`), which capture paths across multiple folder boundaries, see . - -## 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 . The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. - -`Shared/RenderFragmentChild.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: - -> [!IMPORTANT] -> The property receiving the content must be named `ChildContent` by convention. -> -> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for . - -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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: - -Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/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: - -```razor -

    Three children with an index variable

    - -@for (int c = 0; c < 3; c++) -{ - var current = c; - - - Count: @current - -} -``` - -Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: - -```razor -

    Second example of three children with an index variable

    - -@foreach (var c in Enumerable.Range(0,3)) -{ - - Count: @c - -} -``` - -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](xref:blazor/components/layouts) -* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) -* [Templated components](xref:blazor/components/templated-components) -* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) - -> [!NOTE] -> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-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")](/dotnet/api/?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 and render the fragment from any location as many times as needed: - -```razor -

    Hello, world!

    - -@RenderWelcomeInfo - -

    Render the welcome info a second time:

    - -@RenderWelcomeInfo - -@code { - private RenderFragment RenderWelcomeInfo = __builder => - { -

    Welcome to your new app!

    - }; -} -``` - -For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). - -## 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: - -The `Expander` component is added to the following `ExpanderExample` parent component that may call : - -* Calling in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. is covered in more detail later in and . -* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in . - -`Pages/ExpanderExample.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: - -Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. - -If 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 , 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 , so a call to in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. -* The second `Expander` component doesn't set child content. Therefore, a potentially mutable doesn't exist. A call to in the parent component doesn't automatically rerender the child component, so the component's `Expanded` 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 the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: - -For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). - -For more information on change detection, including information on the exact types that Blazor checks, see . - -## 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`][3] 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 `` that supports many parameters. - -In the following `Splat` component: - -* The first `` element (`id="useIndividualParams"`) uses individual component parameters. -* The second `` element (`id="useAttributesDict"`) uses attribute splatting. - -`Pages/Splat.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: - -The rendered `` elements in the webpage are identical: - -```html - - - -``` - -To accept arbitrary attributes, define a [component parameter](#component-parameters) with the property set to `true`: - -```razor -@code { - [Parameter(CaptureUnmatchedValues = true)] - public Dictionary InputAttributes { get; set; } -} -``` - -The property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with . The property type used with must be assignable from [`Dictionary`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. - -The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: - -`Pages/AttributeOrderParent1.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: - -The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `
    ` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): - -```html -
    -``` - -In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `
    `: - -`Shared/AttributeOrderChild2.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: - -`Pages/AttributeOrderParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: - -The `
    ` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: - -```html -
    -``` - -## 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`][4] 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). 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`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: - -The following delegate approach uses the preceding `ReferenceChild` component. - -`Pages/ReferenceParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: - -While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), 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](#component-parameters) section and the article. - -## Synchronization context - -Blazor uses a synchronization context () to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) 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 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 . - -### 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`: - -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/TimerService.cs"::: - -`NotifierService.cs`: - -:::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/NotifierService.cs"::: - -Register the services: - -* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: - - ```csharp - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - ``` - -* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: - - ```csharp - services.AddScoped(); - services.AddScoped(); - ``` - -Use the `NotifierService` to update a component. - -`Pages/ReceiveNotifications.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: - -In the preceding example: - -* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see . -* The component implements . The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see . - -> [!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 () at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/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`][5] directive attribute is required. - -Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. - -For following `Details` and `PeopleExample` components: - -* The `Details` component receives data (`Data`) from the parent `PeopleExample` component, which is displayed in an `` element. Any given displayed `` element can receive the focus of the page from the user when they select one of the `` elements. -* The `PeopleExample` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. - -This demonstration allows you to: - -* Select an `` from among several rendered `Details` components. -* Study the behavior of the page's focus as the people collection automatically grows. - -`Shared/Details.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/Details.razor"::: - -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 `` 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`][5] directive attribute is used to improve the user's experience. - -`Pages/PeopleExample.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: - -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`][5] directive attribute. Use of [`@key`][5] 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`][5] directive attribute with the `people` collection, update the `
    ` element to the following: - -```razor -
    -``` - -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`][5] 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`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. - -You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. - -Example 1: - -```razor -
  • - -
  • -``` - -Example 2: - -```razor -
    - @* other HTML elements *@ -
    -``` - -If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: - -* Discard the entire `
  • ` or `
    ` 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`][5] 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 `
    ` element: - -```razor -
    -
    ...
    -
    ...
    -
    -``` - -The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `
    ` element, not across the parent `
    ` elements: - -```razor -
    -
    ...
    -
    -
    -
    ...
    -
    -``` - -For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: - -```razor -
    - @foreach (var person in people) - { -
    - } -
    -``` - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -The following examples only scope [`@key`][5] to the `
    ` or `
  • ` 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`][5]: - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -### When not to use `@key` - -There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. - -Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] 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`][5]: - -* 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`, or `Guid`. - -Ensure that values used for [`@key`][5] 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`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: - -```razor -@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 `` element's `checked` property is set. - -`Pages/ConditionalAttribute.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: - -For more information, see . - -> [!WARNING] -> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), 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 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 type to add a block of static HTML content to the rendered output of a component. - -`Pages/MarkupStringExample.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: - -## Razor templates - -Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: - -```razor -@<{HTML tag}>... -``` - -The following example illustrates how to specify and values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). - -`Pages/RazorTemplate.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: - -Rendered output of the preceding code: - -```html -

    The time is 4/19/2021 8:54:46 AM.

    -

    Pet: Nutty Rex

    -``` - -## 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](xref:fundamentals/index#web-root) 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. - -```razor -Company logo -``` - -Components do **not** support tilde-slash notation (`~/`). - -For information on setting an app's base path, see . - -## Tag Helpers aren't supported in components - -[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) 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`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `` tag: - -```html -Example image -``` - -Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): - -```css -.element-class { - background-image: url("image.svg"); -} -``` - ## Whitespace rendering behavior +:::moniker range=">= aspnetcore-5.0" + Unless the [`@preservewhitespace`](xref:mvc/views/razor#preservewhitespace) directive is used with a value of `true`, extra whitespace is removed by default if: * Leading or trailing within an element. @@ -3945,1014 +1944,10 @@ Whitespace removal might affect the rendered output when using a CSS rule, such 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`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: - -```razor -@typeparam TItem -``` - -In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. - -`Shared/ListGenericTypeItems1.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: - -The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: - -* String or integer data is assigned to the `ExampleList` parameter of each component. -* Type `string` or `int` that matches the type of the assigned data is set for the type parameter (`TExample`) of each component. - -`Pages/GenericTypeExample1.razor`: - -:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: - -For more information, see the following articles: - -* -* - :::moniker-end :::moniker range="< aspnetcore-5.0" -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](xref:blazor/components/prerendering-and-integration). - -## Component classes - -Components are implemented using a combination of C# and HTML markup in [Razor](xref:mvc/views/razor) component files with the `.razor` file extension. - -By default, is the base class for components described by Razor component files. implements the lowest abstraction of components, the interface. 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](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs): 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. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -Developers typically create Razor components from Razor component files (`.razor`) or base their components on , but components can also be built by implementing . Developer-built components that implement 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](xref:mvc/views/razor). Two Razor features are extensively used by components, *directives* and *directive attributes*. These are reserved keywords prefixed with `@` that appear in Razor markup: - -* [Directives](xref:mvc/views/razor#directives): Change the way component markup is parsed or functions. For example, the [`@page`][9] 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](xref:mvc/views/razor#directive-attributes): Change the way a component element is parsed or functions. For example, the [`@bind`][10] directive attribute for an `` 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 . - -### 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 the `ProductDetail` component has a file name of `ProductDetail.razor` and resides in the `Pages` 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`][9] directive. When a Razor file with an [`@page`][9] directive is compiled, the generated class is given a specifying the route template. At runtime, the router searches for component classes with a 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HelloWorld.razor"::: - -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 . - -### Markup - -A component's UI is defined using [Razor syntax](xref:mvc/views/razor), 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`][1] blocks. In [`@code`][1] 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 value `font-style` of the heading element. -* `headingText` for the content of the heading element. - -`Pages/Markup.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Markup.razor"::: - -> [!NOTE] -> Examples throughout the Blazor documentation specify the [`private` access modifier](/dotnet/csharp/language-reference/keywords/private) 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)](/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers). - -The Blazor framework processes a component internally as a [*render tree*](https://developer.mozilla.org/docs/Web/Performance/How_browsers_work#render), which is the combination of a component's [Document Object Model (DOM)](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) and [Cascading Style Sheet Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model). 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 . - -Razor syntax for C# control structures, directives, and directive attributes are lowercase (examples: [`@if`](xref:mvc/views/razor#conditionals-if-else-if-else-and-switch), [`@code`](xref:mvc/views/razor#code), [`@bind`](xref:mvc/views/razor#bind)). Property names are uppercase (example: `@Body` for ). - -### 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 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Heading.razor"::: - -The following markup in the `HeadingExample` component renders the preceding `Heading` component at the location where the `` tag appears. - -`Pages/HeadingExample.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/HeadingExample.razor"::: - -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`][2] directive for the component's namespace makes the component available, which resolves the warning. For more information, see the [Class name and namespace](#class-name-and-namespace) section. - -The `Heading` component example shown in this section doesn't have an [`@page`][9] 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`][9] 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](/dotnet/csharp/programming-guide/classes-and-structs/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 is `BlazorSample.Pages`. -* The fully qualified type name of the component is `BlazorSample.Pages.Counter`. - -For custom folders that hold components, add an [`@using`][2] directive to the parent component or to the app's `_Imports.razor` file. The following example makes components in the `Components` folder available: - -```razor -@using BlazorSample.Components -``` - -> [!NOTE] -> [`@using`][2] 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`][2] directive. The following example directly references the `ProductDetail` component in the `Components` folder of the app: - -```razor - -``` - -The namespace of a component authored with Razor is based on the following (in priority order): - -* The [`@namespace`][8] directive in the Razor file's markup (for example, `@namespace BlazorSample.CustomNamespace`). -* The project's `RootNamespace` in the project file (for example, `BlazorSample`). -* 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 of `BlazorSample` (`BlazorSample.csproj`) to the namespace `BlazorSample.Pages` for the `Index` component. `{PROJECT ROOT}` is the project root path. Components follow C# name binding rules. For the `Index` 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. - -The following are **not** supported: - -* The [`global::`](/dotnet/csharp/language-reference/operators/namespace-alias-qualifier) qualification. -* Importing components with aliased [`using`](/dotnet/csharp/language-reference/keywords/using-statement) 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 the `NavMenu` component in the app's `Shared` folder (`Shared/NavMenu.razor`) with ``. - -### Partial class support - -Components are generated as [C# partial classes](/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) and are authored using either of the following approaches: - -* A single file contains C# code defined in one or more [`@code`][1] 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`][1] 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/Counter.razor"::: - -The following `Counter` component splits HTML and Razor markup from C# code using a code-behind file with a partial class: - -`Pages/CounterPartialClass.razor`: - -```razor -@page "/counter-partial-class" - -

    Counter

    - -

    Current count: @currentCount

    - - -``` - -`Pages/CounterPartialClass.razor.cs`: - -```csharp -namespace BlazorSample.Pages -{ - public partial class CounterPartialClass - { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } - } -} -``` - -[`@using`][2] 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: - -```csharp -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: - -```csharp -using BlazorSample; -using BlazorSample.Shared; -``` - -### Specify a base class - -The [`@inherits`][6] 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 . - -`Pages/BlazorRocks.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/BlazorRocks.razor"::: - -`BlazorRocksBase.cs`: - -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/BlazorRocksBase.cs"::: - -## Component parameters - -*Component parameters* pass data to components and are defined using public [C# properties](/dotnet/csharp/programming-guide/classes-and-structs/properties) on the component class with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute). In the following example, a built-in reference type () and a user-defined reference type (`PanelBody`) are passed as component parameters. - -`PanelBody.cs`: - -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/PanelBody.cs"::: - -`Shared/ParameterChild.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ParameterChild.razor"::: - -> [!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](#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 for `Title` and `Body` from the `ParameterParent` component, which uses an [explicit C# expression](xref:mvc/views/razor#explicit-razor-expressions) to set the values of the `PanelBody`'s properties. - -`Pages/ParameterParent.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent.razor"::: - -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. - -```html -

    Child component (without attribute values)

    - -
    -
    Set By Child
    -
    Set by child.
    -
    - -

    Child component (with attribute values)

    - -
    -
    Set by Parent
    -
    Set by parent.
    -
    -``` - -Assign a C# field, property, or result of a method to a component parameter as an HTML attribute value. The value of the attribute can typically be any C# expression that matches the type of the parameter. The value of the attribute can optionally lead with a [Razor reserved `@` symbol](xref:mvc/views/razor#razor-syntax), but it isn't required. - -If the component parameter is of type string, then the attribute value is instead treated as a C# string literal by default. If you want to specify a C# expression instead, then use the `@` prefix. - -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 , which uses an [implicit C# expression](xref:mvc/views/razor#implicit-razor-expressions). -* The `panelData` object's `Title` property. - -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"`. -* Use the `@` prefix with nonliterals, ***even when it's optional***. Example: `Count="@ct"`, where `ct` is a number-typed variable. `Count="ct"` is a valid stylistic approach, but the documentation and examples don't adopt the convention. -* Always avoid `@` for literals, outside of Razor expressions. Example: `IsFixed="true"`. - -`Pages/ParameterParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent2.razor"::: - -> [!NOTE] -> When assigning a C# member to a component parameter, don't prefix the parameter's HTML attribute with `@`. -> -> Correct (`Title` is a string parameter, `Count` is a number-typed parameter): -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` -> -> Incorrect: -> -> ```razor -> -> ``` -> -> ```razor -> -> ``` - -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](xref:blazor/components/lifecycle). After each asynchronous lifecycle event, the component may render again. The following Razor syntax is **not** supported: - -```razor - -``` - -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](xref:blazor/components/lifecycle#component-initialization-oninitializedasync), as the following example demonstrates: - -```razor - - -@code { - private string title; - - protected override async Task OnInitializedAsync() - { - title = await ...; - } -} -``` - -For more information, see . - -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: - -```razor - -``` - -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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ParameterParent3.razor"::: - -For more information, see . - -> [!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](#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: - -```csharp -[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`](xref:blazor/components/lifecycle#after-parameters-are-set-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 with to `StartData` is valid syntax in a component: - -```csharp -[Parameter] -public DateTime StartData { get; set; } = DateTime.Now; -``` - -After the initial assignment of , do **not** assign a value to `StartData` in developer code. For more information, see the [Overwritten parameters](#overwritten-parameters) section of this article. - -## Route parameters - -Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. - -`Pages/RouteParameter.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RouteParameter.razor" highlight="2,7-8"::: - -Optional route parameters aren't supported, so two [`@page`][9] directives are applied in the preceding example. The first [`@page`][9] directive permits navigation to the component without a route parameter. The second [`@page`][9] 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 . - -## 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 . The position of `ChildContent` in the component's Razor markup is where the content is rendered in the final HTML output. - -`Shared/RenderFragmentChild.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/RenderFragmentChild.razor"::: - -> [!IMPORTANT] -> The property receiving the content must be named `ChildContent` by convention. -> -> [Event callbacks](xref:blazor/components/event-handling#eventcallback) aren't supported for . - -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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RenderFragmentParent.razor"::: - -Due to the way that Blazor renders child content, rendering components inside a [`for`](/dotnet/csharp/language-reference/keywords/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: - -```razor -

    Three children with an index variable

    - -@for (int c = 0; c < 3; c++) -{ - var current = c; - - - Count: @current - -} -``` - -Alternatively, use a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) loop with instead of a [`for`](/dotnet/csharp/language-reference/keywords/for) loop. The following example can be added to the preceding `RenderFragmentParent` component: - -```razor -

    Second example of three children with an index variable

    - -@foreach (var c in Enumerable.Range(0,3)) -{ - - Count: @c - -} -``` - -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](xref:blazor/components/layouts) -* [Pass data across a component hierarchy](xref:blazor/components/cascading-values-and-parameters#pass-data-across-a-component-hierarchy) -* [Templated components](xref:blazor/components/templated-components) -* [Global exception handling](xref:blazor/fundamentals/handle-errors#global-exception-handling) - -> [!NOTE] -> Blazor framework's [built-in Razor components](xref:blazor/components/built-in-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")](/dotnet/api/?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 and render the fragment from any location as many times as needed: - -```razor -

    Hello, world!

    - -@RenderWelcomeInfo - -

    Render the welcome info a second time:

    - -@RenderWelcomeInfo - -@code { - private RenderFragment RenderWelcomeInfo = __builder => - { -

    Welcome to your new app!

    - }; -} -``` - -For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code). - -## 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/BadExpander.razor"::: - -The `Expander` component is added to the following `ExpanderExample` parent component that may call : - -* Calling in developer code notifies a component that its state has changed and typically triggers component rerendering to update the UI. is covered in more detail later in and . -* The button's `@onclick` directive attribute attaches an event handler to the button's `onclick` event. Event handling is covered in more detail later in . - -`Pages/ExpanderExample.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ExpanderExample.razor"::: - -Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. - -If 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 , 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 , so a call to in the parent component automatically rerenders the component and potentially overwrites the value of `Expanded` to its initial value of `true`. -* The second `Expander` component doesn't set child content. Therefore, a potentially mutable doesn't exist. A call to in the parent component doesn't automatically rerender the child component, so the component's `Expanded` 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 the [`OnInitialized` event](xref:blazor/components/lifecycle#component-initialization-oninitializedasync). -* 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Expander.razor"::: - -For additional information, see [Blazor Two Way Binding Error (dotnet/aspnetcore #24599)](https://github.com/dotnet/aspnetcore/issues/24599). - -For more information on change detection, including information on the exact types that Blazor checks, see . - -## 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`][3] 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 `` that supports many parameters. - -In the following `Splat` component: - -* The first `` element (`id="useIndividualParams"`) uses individual component parameters. -* The second `` element (`id="useAttributesDict"`) uses attribute splatting. - -`Pages/Splat.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/Splat.razor"::: - -The rendered `` elements in the webpage are identical: - -```html - - - -``` - -To accept arbitrary attributes, define a [component parameter](#component-parameters) with the property set to `true`: - -```razor -@code { - [Parameter(CaptureUnmatchedValues = true)] - public Dictionary InputAttributes { get; set; } -} -``` - -The property on [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) allows the parameter to match all attributes that don't match any other parameter. A component can only define a single parameter with . The property type used with must be assignable from [`Dictionary`](xref:System.Collections.Generic.Dictionary%602) with string keys. Use of [`IEnumerable>`](xref:System.Collections.Generic.IEnumerable%601) or [`IReadOnlyDictionary`](xref:System.Collections.Generic.IReadOnlyDictionary%602) are also options in this scenario. - -The position of [`@attributes`][3] relative to the position of element attributes is important. When [`@attributes`][3] 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild1.razor"::: - -`Pages/AttributeOrderParent1.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent1.razor"::: - -The `AttributeOrderChild1` component's `extra` attribute is set to the right of [`@attributes`][3]. The `AttributeOrderParent1` component's rendered `
    ` contains `extra="5"` when passed through the additional attribute because the attributes are processed right to left (last to first): - -```html -
    -``` - -In the following example, the order of `extra` and [`@attributes`][3] is reversed in the child component's `
    `: - -`Shared/AttributeOrderChild2.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/AttributeOrderChild2.razor"::: - -`Pages/AttributeOrderParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/AttributeOrderParent2.razor"::: - -The `
    ` in the parent component's rendered webpage contains `extra="10"` when passed through the additional attribute: - -```html -
    -``` - -## 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`][4] 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ReferenceChild.razor"::: - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). - -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](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync). 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`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent1.razor"::: - -The following delegate approach uses the preceding `ReferenceChild` component. - -`Pages/ReferenceParent2.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReferenceParent2.razor"::: - -While capturing component references use a similar syntax to [capturing element references](xref:blazor/js-interop/call-javascript-from-dotnet#capture-references-to-elements), 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](#component-parameters) section and the article. - -## Synchronization context - -Blazor uses a synchronization context () to enforce a single logical thread of execution. A component's [lifecycle methods](xref:blazor/components/lifecycle) 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 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 . - -### 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`: - -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/TimerService.cs"::: - -`NotifierService.cs`: - -:::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/NotifierService.cs"::: - -Register the services: - -* In a Blazor WebAssembly app, register the services as singletons in `Program.cs`: - - ```csharp - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - ``` - -* In a Blazor Server app, register the services as scoped in `Startup.ConfigureServices`: - - ```csharp - services.AddScoped(); - services.AddScoped(); - ``` - -Use the `NotifierService` to update a component. - -`Pages/ReceiveNotifications.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ReceiveNotifications.razor"::: - -In the preceding example: - -* `NotifierService` invokes the component's `OnNotify` method outside of Blazor's synchronization context. `InvokeAsync` is used to switch to the correct context and queue a render. For more information, see . -* The component implements . The `OnNotify` delegate is unsubscribed in the `Dispose` method, which is called by the framework when the component is disposed. For more information, see . - -> [!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 () at the time the handler is registered. For more information, see [Calling `InvokeAsync(StateHasChanged)` causes page to fallback to default culture (dotnet/aspnetcore #28521)](https://github.com/dotnet/aspnetcore/issues/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`][5] directive attribute is required. - -Consider the following example that demonstrates a collection mapping problem that's solved by using [`@key`][5]. - -For the following `Details` and `PeopleExample` components: - -* The `Details` component receives data (`Data`) from the parent `PeopleExample` component, which is displayed in an `` element. Any given displayed `` element can receive the focus of the page from the user when they select one of the `` elements. -* The `PeopleExample` component creates a list of person objects for display using the `Details` component. Every three seconds, a new person is added to the collection. - -This demonstration allows you to: - -* Select an `` from among several rendered `Details` components. -* Study the behavior of the page's focus as the people collection automatically grows. - -`Shared/Details.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/Details.razor"::: - -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 `` 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`][5] directive attribute is used to improve the user's experience. - -`Pages/PeopleExample.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/PeopleExample.razor"::: - -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`][5] directive attribute. Use of [`@key`][5] 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`][5] directive attribute with the `people` collection, update the `
    ` element to the following: - -```razor -
    -``` - -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`][5] 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`][5] whenever a list is rendered (for example, in a [`foreach`](/dotnet/csharp/language-reference/keywords/foreach-in) block) and a suitable value exists to define the [`@key`][5]. - -You can also use [`@key`][5] to preserve an element or component subtree when an object doesn't change, as the following examples show. - -Example 1: - -```razor -
  • - -
  • -``` - -Example 2: - -```razor -
    - @* other HTML elements *@ -
    -``` - -If an `person` instance changes, the [`@key`][5] attribute directive forces Blazor to: - -* Discard the entire `
  • ` or `
    ` 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`][5] 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 `
    ` element: - -```razor -
    -
    ...
    -
    ...
    -
    -``` - -The following example demonstrates `first` and `second` keys in their own scopes, unrelated to each other and without influence on each other. Each [`@key`][5] scope only applies to its parent `
    ` element, not across the parent `
    ` elements: - -```razor -
    -
    ...
    -
    -
    -
    ...
    -
    -``` - -For the `Details` component shown earlier, the following examples render `person` data within the same [`@key`][5] scope and demonstrate typical use cases for [`@key`][5]: - -```razor -
    - @foreach (var person in people) - { -
    - } -
    -``` - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -The following examples only scope [`@key`][5] to the `
    ` or `
  • ` 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`][5]: - -```razor -@foreach (var person in people) -{ -
    -
    -
    -} -``` - -```razor -
      - @foreach (var person in people) - { -
    1. -
      -
    2. - } -
    -``` - -### When not to use `@key` - -There's a performance cost when rendering with [`@key`][5]. The performance cost isn't large, but only specify [`@key`][5] if preserving the element or component benefits the app. - -Even if [`@key`][5] isn't used, Blazor preserves child element and component instances as much as possible. The only advantage to using [`@key`][5] 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`][5]: - -* 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`, or `Guid`. - -Ensure that values used for [`@key`][5] 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`][7] directive. The following example applies the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the component's class: - -```razor -@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 `` element's `checked` property is set. - -`Pages/ConditionalAttribute.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/ConditionalAttribute.razor"::: - -For more information, see . - -> [!WARNING] -> Some HTML attributes, such as [`aria-pressed`](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Roles/button_role#Toggle_buttons), 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 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 type to add a block of static HTML content to the rendered output of a component. - -`Pages/MarkupStringExample.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/MarkupStringExample.razor"::: - -## Razor templates - -Render fragments can be defined using Razor template syntax to define a UI snippet. Razor templates use the following format: - -```razor -@<{HTML tag}>... -``` - -The following example illustrates how to specify and values and render templates directly in a component. Render fragments can also be passed as arguments to [templated components](xref:blazor/components/templated-components). - -`Pages/RazorTemplate.razor`: - -:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/RazorTemplate.razor"::: - -Rendered output of the preceding code: - -```html -

    The time is 4/19/2021 8:54:46 AM.

    -

    Pet: Nutty Rex

    -``` - -## 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](xref:fundamentals/index#web-root) 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. - -```razor -Company logo -``` - -Components do **not** support tilde-slash notation (`~/`). - -For information on setting an app's base path, see . - -## Tag Helpers aren't supported in components - -[`Tag Helpers`](xref:mvc/views/tag-helpers/intro) 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`)](https://developer.mozilla.org/docs/Web/SVG), are supported via the `` tag: - -```html -Example image -``` - -Similarly, SVG images are supported in the CSS rules of a stylesheet file (`.css`): - -```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: @@ -4988,6 +1983,8 @@ Whitespace isn't preserved from the preceding markup: Example image ``` +:::moniker-end + ## Generic type parameter support The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csharp/programming-guide/generics/generic-type-parameters) for the generated component class: @@ -4996,12 +1993,40 @@ The [`@typeparam`][11] directive declares a [generic type parameter](/dotnet/csh @typeparam TItem ``` +C# syntax with [`where`](/dotnet/csharp/language-reference/keywords/where-generic-type-constraint) type constraints is supported: + +```razor +@typeparam TEntity where TEntity : IEntity +``` + In the following example, the `ListGenericTypeItems1` component is generically typed as `TExample`. `Shared/ListGenericTypeItems1.razor`: +:::moniker range=">= aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/index/ListGenericTypeItems1.razor"::: +:::moniker-end + The following `GenericTypeExample1` component renders two `ListGenericTypeItems1` components: * String or integer data is assigned to the `ExampleList` parameter of each component. @@ -5009,12 +2034,379 @@ The following `GenericTypeExample1` component renders two `ListGenericTypeItems1 `Pages/GenericTypeExample1.razor`: +:::moniker range=">= aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" + +:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: + +:::moniker-end + +:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" + +:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: + +:::moniker-end + +:::moniker range="< aspnetcore-5.0" + :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/GenericTypeExample1.razor"::: -For more information, see the following articles: +:::moniker-end -* -* +For more information, see . For an example of generic typing with templated components, see . + +:::moniker range=">= aspnetcore-6.0" + +## Cascaded generic type support + +An ancestor component can cascade a type parameter by name to descendants using the [`[CascadingTypeParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute). 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`](xref:mvc/views/razor#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 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`: + +```razor +@typeparam TExample + +@if (ExampleList is not null) +{ +
      + @foreach (var item in ExampleList) + { +
    • @item
    • + } +
    +} + +@code { + [Parameter] + public IEnumerable? ExampleList { get; set; } +} +``` + +`Shared/ListDisplay2.razor`: + +```razor +@typeparam TExample + +@if (ExampleList is not null) +{ +
      + @foreach (var item in ExampleList) + { +
    • @item
    • + } +
    +} + +@code { + [Parameter] + public IEnumerable? 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](#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`: + +```razor +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample + +

    List Generic Type Items 2

    + +@ChildContent + +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } +} +``` + +The following `GenericTypeExample2` parent component sets the child content () 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`: + +```razor +@page "/generic-type-example-2" + +

    Generic Type Example 2

    + + + + + + + + + + +``` + +Specifying the type explicitly also allows the use of [cascading values and parameters](xref:blazor/components/cascading-values-and-parameters) to provide data to child components, as the following demonstration shows. + +`Shared/ListDisplay3.razor`: + +```razor +@typeparam TExample + +@if (ExampleList is not null) +{ +
      + @foreach (var item in ExampleList) + { +
    • @item
    • + } +
    +} + +@code { + [CascadingParameter] + protected IEnumerable? ExampleList { get; set; } +} +``` + +`Shared/ListDisplay4.razor`: + +```razor +@typeparam TExample + +@if (ExampleList is not null) +{ +
      + @foreach (var item in ExampleList) + { +
    • @item
    • + } +
    +} + +@code { + [CascadingParameter] + protected IEnumerable? ExampleList { get; set; } +} +``` + +`Shared/ListGenericTypeItems3.razor`: + +```razor +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample + +

    List Generic Type Items 3

    + +@ChildContent + +@if (ExampleList is not null) +{ +
      + @foreach(var item in ExampleList) + { +
    • @item
    • + } +
    + +

    + Type of TExample: @typeof(TExample) +

    +} + +@code { + [CascadingParameter] + protected IEnumerable? 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`: + +```razor +@page "/generic-type-example-3" + +

    Generic Type Example 3

    + + + + + + + + + + + + + + + +@code { + private List stringData = new() { "Item 1", "Item 2" }; + private List 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: + +```razor + +``` + +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: + +```razor + +``` + +### 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](#cascaded-generic-type-support) section. + +`Shared/ListGenericTypeItems4.razor`: + +```razor +@attribute [CascadingTypeParameter(nameof(TExample))] +@typeparam TExample + +

    List Generic Type Items 4

    + +@ChildContent + +@if (ExampleList is not null) +{ +
      + @foreach(var item in ExampleList) + { +
    • @item
    • + } +
    + +

    + Type of TExample: @typeof(TExample) +

    +} + +@code { + [Parameter] + public IEnumerable? 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`: + +```razor +@page "/generic-type-example-4" + +

    Generic Type Example 4

    + + + + + + + + + + +``` + +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`: + +```razor +@page "/generic-type-example-5" + +

    Generic Type Example 5

    + + + + + + + + + + + +@code { + private List stringData = new() { "Item 1", "Item 2" }; + private List 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](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper): + +```cshtml + +``` + +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`: + +```csharp +builder.RootComponents.Add("#app"); +``` + +In the preceding code, the CSS selector, `#app`, indicates that the `App` component is created for the `
    ` in `wwwroot/index.html` with an `id` of `app`: + +```html +
    ... +``` + +MVC and Razor Pages apps can also use the [Component Tag Helper](xref:Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper) to register statically-rendered Blazor WebAssembly root components: + +```cshtml + +``` + +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: + +* +* :::moniker-end