--- title: ASP.NET Core Razor component lifecycle author: guardrex description: Learn about the ASP.NET Core Razor component lifecycle and how to use lifecycle events. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 uid: blazor/components/lifecycle --- # ASP.NET Core Razor component lifecycle [!INCLUDE[](~/includes/not-latest-version.md)] This article explains the ASP.NET Core Razor component lifecycle and how to use lifecycle events. ## Lifecycle events The Razor component processes Razor component lifecycle events in a set of synchronous and asynchronous lifecycle methods. The lifecycle methods can be overridden to perform additional operations in components during component initialization and rendering. This article simplifies component lifecycle event processing in order to clarify complex framework logic and doesn't cover every change that was made over the years. You may need to access the [`ComponentBase` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs) to integrate custom event processing with Blazor's lifecycle event processing. Code comments in the reference source include additional remarks on lifecycle event processing that don't appear in this article or in the [API documentation](/dotnet/api/). [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] The following simplified diagrams illustrate Razor component lifecycle event processing. The C# methods associated with the lifecycle events are defined with examples in the following sections of this article. Component lifecycle events: 1. If the component is rendering for the first time on a request: * Create the component's instance. * Perform property injection. * Call [`OnInitialized{Async}`](#component-initialization-oninitializedasync). If an incomplete is returned, the is awaited and then the component is rerendered. The synchronous method is called prior to the asynchronous method. 1. Call [`OnParametersSet{Async}`](#after-parameters-are-set-onparameterssetasync). If an incomplete is returned, the is awaited and then the component is rerendered. The synchronous method is called prior to the asynchronous method. 1. Render for all synchronous work and complete s. > [!NOTE] > Asynchronous actions performed in lifecycle events might delay component rendering or displaying data. For more information, see the [Handle incomplete asynchronous actions at render](#handle-incomplete-asynchronous-actions-at-render) section later in this article. A parent component renders before its children components because rendering is what determines which children are present. If synchronous parent component initialization is used, the parent initialization is guaranteed to complete first. If asynchronous parent component initialization is used, the completion order of parent and child component initialization can't be determined because it depends on the initialization code running. ![Component lifecycle events of a Razor component in Blazor](~/blazor/components/lifecycle/_static/lifecycle1.png) DOM event processing: 1. The event handler is run. 1. If an incomplete is returned, the is awaited and then the component is rerendered. 1. Render for all synchronous work and complete s. ![DOM event processing](~/blazor/components/lifecycle/_static/lifecycle2.png) The `Render` lifecycle: 1. Avoid further rendering operations on the component when both of the following conditions are met: * It is not the first render. * [`ShouldRender`](xref:blazor/components/rendering#suppress-ui-refreshing-shouldrender) returns `false`. 1. Build the render tree diff (difference) and render the component. 1. Await the DOM to update. 1. Call [`OnAfterRender{Async}`](#after-component-render-onafterrenderasync). The synchronous method is called prior to the asynchronous method. ![Render lifecycle](~/blazor/components/lifecycle/_static/lifecycle3.png) Developer calls to [`StateHasChanged`](#state-changes-statehaschanged) result in a rerender. For more information, see . ## Quiescence during prerendering In server-side Blazor apps, prerendering waits for *quiescence*, which means that a component doesn't render until all of the components in the render tree have finished rendering. Quiescence can lead to noticeable delays in rendering when a component performs long-running tasks during initialization and other lifecycle methods, leading to a poor user experience. For more information, see the [Handle incomplete asynchronous actions at render](#handle-incomplete-asynchronous-actions-at-render) section later in this article. ## When parameters are set (`SetParametersAsync`) sets parameters supplied by the component's parent in the render tree or from route parameters. The method's parameter contains the set of [component parameter](xref:blazor/components/index#component-parameters) values for the component each time is called. By overriding the method, developer code can interact directly with 's parameters. The default implementation of sets the value of each property with the [`[Parameter]`](xref:Microsoft.AspNetCore.Components.ParameterAttribute) or [`[CascadingParameter]` attribute](xref:Microsoft.AspNetCore.Components.CascadingParameterAttribute) that has a corresponding value in the . Parameters that don't have a corresponding value in are left unchanged. Generally, your code should call the base class method (`await base.SetParametersAsync(parameters);`) when overriding . In advanced scenarios, developer code can interpret the incoming parameters' values in any way required by not invoking the base class method. For example, there's no requirement to assign the incoming parameters to the properties of the class. However, you must refer to the [`ComponentBase` reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Components/src/ComponentBase.cs) when structuring your code without calling the base class method because it calls other lifecycle methods and triggers rendering in a complex fashion. [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] If you want to rely on the initialization and rendering logic of but not process incoming parameters, you have the option of passing an empty to the base class method: ```csharp await base.SetParametersAsync(ParameterView.Empty); ``` If event handlers are provided in developer code, unhook them on disposal. For more information, see the [Component disposal with `IDisposable` and `IAsyncDisposable`](#component-disposal-with-idisposable-and-iasyncdisposable) section. In the following example, assigns the `Param` parameter's value to `value` if parsing a route parameter for `Param` is successful. When `value` isn't `null`, the value is displayed by the component. Although [route parameter matching is case insensitive](xref:blazor/fundamentals/routing#route-parameters), only matches case-sensitive parameter names in the route template. The following example requires the use of `/{Param?}` in the route template in order to get the value with , not `/{param?}`. If `/{param?}` is used in this scenario, returns `false` and `message` isn't set to either `message` string. `SetParamsAsync.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/SetParamsAsync.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/SetParamsAsync.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/SetParamsAsync.razor"::: :::moniker-end ## Component initialization (`OnInitialized{Async}`) and are used exclusively to initialize a component for the entire lifetime of the component instance. Parameter values and parameter value changes shouldn't affect the initialization performed in these methods. For example, loading static options into a dropdown list that doesn't change for the lifetime of the component and that isn't dependent on parameter values is performed in one of these lifecycle methods. If parameter values or changes in parameter values affect component state, use [`OnParametersSet{Async}`](#when-parameters-are-set-setparametersasync) instead. These methods are invoked when the component is initialized after having received its initial parameters in . The synchronous method is called prior to the asynchronous method. If synchronous parent component initialization is used, the parent initialization is guaranteed to complete before child component initialization. If asynchronous parent component initialization is used, the completion order of parent and child component initialization can't be determined because it depends on the initialization code running. For a synchronous operation, override : `OnInit.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/OnInit.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/OnInit.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnInit.razor"::: :::moniker-end To perform an asynchronous operation, override and use the [`await`](/dotnet/csharp/language-reference/operators/await) operator: ```csharp protected override async Task OnInitializedAsync() { await ... } ``` If a custom [base class](xref:blazor/components/index#specify-a-base-class) is used with custom initialization logic, call on the base class: ```csharp protected override async Task OnInitializedAsync() { await ... await base.OnInitializedAsync(); } ``` It isn't necessary to call unless a custom base class is used with custom logic. For more information, see the [Base class lifecycle methods](#base-class-lifecycle-methods) section. Blazor apps that prerender their content on the server call *twice*: * Once when the component is initially rendered statically as part of the page. * A second time when the browser renders the component. :::moniker range=">= aspnetcore-8.0" To prevent developer code in from running twice when prerendering, see the [Stateful reconnection after prerendering](#stateful-reconnection-after-prerendering) section. The content in the section focuses on Blazor Web Apps and stateful SignalR *reconnection*. To preserve state during the execution of initialization code while prerendering, see . :::moniker-end :::moniker range="< aspnetcore-8.0" To prevent developer code in from running twice when prerendering, see the [Stateful reconnection after prerendering](#stateful-reconnection-after-prerendering) section. Although the content in the section focuses on Blazor Server and stateful SignalR *reconnection*, the scenario for prerendering in hosted Blazor WebAssembly solutions () involves similar conditions and approaches to prevent executing developer code twice. To preserve state during the execution of initialization code while prerendering, see . :::moniker-end While a Blazor app is prerendering, certain actions, such as calling into JavaScript (JS interop), aren't possible. Components may need to render differently when prerendered. For more information, see the [Prerendering with JavaScript interop](#prerendering-with-javascript-interop) section. If event handlers are provided in developer code, unhook them on disposal. For more information, see the [Component disposal with `IDisposable` `IAsyncDisposable`](#component-disposal-with-idisposable-and-iasyncdisposable) section. ::: moniker range=">= aspnetcore-8.0" Use *streaming rendering* with static server-side rendering (static SSR) or prerendering to improve the user experience for components that perform long-running asynchronous tasks in to fully render. For more information, see the following resources: * [Handle incomplete asynchronous actions at render](#handle-incomplete-asynchronous-actions-at-render) (this article) * :::moniker-end ## After parameters are set (`OnParametersSet{Async}`) or are called: * After the component is initialized in or . * When the parent component rerenders and supplies: * Known or primitive immutable types when at least one parameter has changed. * Complex-typed parameters. The framework can't know whether the values of a complex-typed parameter have mutated internally, so the framework always treats the parameter set as changed when one or more complex-typed parameters are present. For more information on rendering conventions, see . The synchronous method is called prior to the asynchronous method. The methods can be invoked even if the parameter values haven't changed. This behavior underscores the need for developers to implement additional logic within the methods to check whether parameter values have indeed changed before re-initializing data or state dependent on those parameters. For the following example component, navigate to the component's page at a URL: * With a start date that's received by `StartDate`: `/on-parameters-set/2021-03-19` * Without a start date, where `StartDate` is assigned a value of the current local time: `/on-parameters-set` :::moniker range=">= aspnetcore-5.0" > [!NOTE] > In a component route, it isn't possible to both constrain a parameter with the [route constraint `datetime`](xref:blazor/fundamentals/routing#route-constraints) and [make the parameter optional](xref:blazor/fundamentals/routing#route-parameters). Therefore, the following `OnParamsSet` component uses two [`@page`](xref:mvc/views/razor#page) directives to handle routing with and without a supplied date segment in the URL. :::moniker-end `OnParamsSet.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/OnParamsSet.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/OnParamsSet.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/OnParamsSet.razor"::: :::moniker-end Asynchronous work when applying parameters and property values must occur during the lifecycle event: ```csharp protected override async Task OnParametersSetAsync() { await ... } ``` If a custom [base class](xref:blazor/components/index#specify-a-base-class) is used with custom initialization logic, call on the base class: ```csharp protected override async Task OnParametersSetAsync() { await ... await base.OnParametersSetAsync(); } ``` It isn't necessary to call unless a custom base class is used with custom logic. For more information, see the [Base class lifecycle methods](#base-class-lifecycle-methods) section. If event handlers are provided in developer code, unhook them on disposal. For more information, see the [Component disposal with `IDisposable` `IAsyncDisposable`](#component-disposal-with-idisposable-and-iasyncdisposable) section. For more information on route parameters and constraints, see . For an example of implementing `SetParametersAsync` manually to improve performance in some scenarios, see . ## After component render (`OnAfterRender{Async}`) :::moniker range=">= aspnetcore-8.0" and are invoked after a component has rendered interactively and the UI has finished updating (for example, after elements are added to the browser DOM). Element and component references are populated at this point. Use this stage to perform additional initialization steps with the rendered content, such as JS interop calls that interact with the rendered DOM elements. The synchronous method is called prior to the asynchronous method. These methods aren't invoked during prerendering or static server-side rendering (static SSR) on the server because those processes aren't attached to a live browser DOM and are already complete before the DOM is updated. For , the component doesn't automatically rerender after the completion of any returned `Task` to avoid an infinite render loop. :::moniker-end :::moniker range="< aspnetcore-8.0" and are called after a component has finished rendering. Element and component references are populated at this point. Use this stage to perform additional initialization steps with the rendered content, such as JS interop calls that interact with the rendered DOM elements. The synchronous method is called prior to the asynchronous method. These methods aren't invoked during prerendering because prerendering isn't attached to a live browser DOM and is already complete before the DOM is updated. For , the component doesn't automatically rerender after the completion of any returned `Task` to avoid an infinite render loop. :::moniker-end The `firstRender` parameter for and : * Is set to `true` the first time that the component instance is rendered. * Can be used to ensure that initialization work is only performed once. `AfterRender.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/AfterRender.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/AfterRender.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/AfterRender.razor"::: :::moniker-end The `AfterRender.razor` sample produces following output to console when the page is loaded and the button is selected: > :::no-loc text="OnAfterRender: firstRender = True"::: > :::no-loc text="HandleClick called"::: > :::no-loc text="OnAfterRender: firstRender = False"::: Asynchronous work immediately after rendering must occur during the lifecycle event: ```csharp protected override async Task OnAfterRenderAsync(bool firstRender) { ... } ``` If a custom [base class](xref:blazor/components/index#specify-a-base-class) is used with custom initialization logic, call on the base class: ```csharp protected override async Task OnAfterRenderAsync(bool firstRender) { ... await base.OnAfterRenderAsync(firstRender); } ``` It isn't necessary to call unless a custom base class is used with custom logic. For more information, see the [Base class lifecycle methods](#base-class-lifecycle-methods) section. Even if you return a from , the framework doesn't schedule a further render cycle for your component once that task completes. This is to avoid an infinite render loop. This is different from the other lifecycle methods, which schedule a further render cycle once a returned completes. and *aren't called during the prerendering process on the server*. The methods are called when the component is rendered interactively after prerendering. When the app prerenders: 1. The component executes on the server to produce some static HTML markup in the HTTP response. During this phase, and aren't called. 1. When the Blazor script (`blazor.{server|webassembly|web}.js`) starts in the browser, the component is restarted in an interactive rendering mode. After a component is restarted, and **are** called because the app isn't in the prerendering phase any longer. If event handlers are provided in developer code, unhook them on disposal. For more information, see the [Component disposal with `IDisposable` `IAsyncDisposable`](#component-disposal-with-idisposable-and-iasyncdisposable) section. ## Base class lifecycle methods When overriding Blazor's lifecycle methods, it isn't necessary to call base class lifecycle methods for . However, a component should call an overridden base class lifecycle method in the following situations: * When overriding , `await base.SetParametersAsync(parameters);` is usually invoked because the base class method calls other lifecycle methods and triggers rendering in a complex fashion. For more information, see the [When parameters are set (`SetParametersAsync`)](#when-parameters-are-set-setparametersasync) section. * If the base class method contains logic that must be executed. Library consumers usually call base class lifecycle methods when inheriting a base class because library base classes often have custom lifecycle logic to execute. If the app uses a base class from a library, consult the library's documentation for guidance. In the following example, `base.OnInitialized();` is called to ensure that the base class's `OnInitialized` method is executed. Without the call, `BlazorRocksBase2.OnInitialized` doesn't execute. `BlazorRocks2.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/BlazorRocks2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/BlazorRocks2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/index/BlazorRocks2.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/index/BlazorRocks2.razor"::: :::moniker-end `BlazorRocksBase2.cs`: :::moniker range=">= aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/BlazorRocksBase2.cs"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/BlazorRocksBase2.cs"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/BlazorRocksBase2.cs"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/BlazorRocksBase2.cs"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/BlazorRocksBase2.cs"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/BlazorRocksBase2.cs"::: :::moniker-end ## State changes (`StateHasChanged`) notifies the component that its state has changed. When applicable, calling enqueues a rerender that occurs when the app's main thread is free. is called automatically for methods. For more information on event callbacks, see . For more information on component rendering and when to call , including when to invoke it with , see . ## Handle incomplete asynchronous actions at render Asynchronous actions performed in lifecycle events might not have completed before the component is rendered. Objects might be `null` or incompletely populated with data while the lifecycle method is executing. Provide rendering logic to confirm that objects are initialized. Render placeholder UI elements (for example, a loading message) while objects are `null`. In the following `Slow` component, is overridden to asynchronously execute a long-running task. While `isLoading` is `true`, a loading message is displayed to the user. After the `Task` returned by completes, the component is rerendered with the updated state, showing the "`Finished!`" message. `Slow.razor`: ```razor @page "/slow"

Slow Component

@if (isLoading) {
Loading...
} else {
Finished!
} @code { private bool isLoading = true; protected override async Task OnInitializedAsync() { await LoadDataAsync(); isLoading = false; } private Task LoadDataAsync() { return Task.Delay(10000); } } ``` The preceding component uses an `isLoading` variable to display the loading message. A similar approach is used for a component that loads data into a collection and checks if the collection is `null` to present the loading message. The following example checks the `movies` collection for `null` to either display the loading message or display the collection of movies: ```razor @if (movies == null) {

Loading...

} else { @* display movies *@ } @code { private Movies[]? movies; protected override async Task OnInitializedAsync() { movies = await GetMovies(); } } ``` Prerendering waits for *quiescence*, which means that a component doesn't render until all of the components in the render tree have finished rendering. This means that a loading message doesn't display while a child component's method is executing a long-running task during prerendering. To demonstrate this behavior, place the preceding `Slow` component into a test app's `Home` component: ```razor @page "/" Home

Hello, world!

Welcome to your new app. ``` > [!NOTE] > Although the examples in this section discuss the lifecycle method, other lifecycle methods that execute during prerendering may delay final rendering of a component. Only [`OnAfterRender{Async}`](#after-component-render-onafterrenderasync) isn't executed during prerendering and is immune to delays due to quiescence. During prerendering, the `Home` component doesn't render until the `Slow` component is rendered, which takes ten seconds. The UI is blank during this ten-second period, and there's no loading message. After prerendering, the `Home` component renders, and the `Slow` component's loading message is displayed. After ten more seconds, the `Slow` component finally displays the finished message. :::moniker range=">= aspnetcore-8.0" As the preceding demonstration illustrates, quiescence during prerendering results in a poor user experience. To improve the user experience, begin by implementing [streaming rendering](xref:blazor/components/rendering#streaming-rendering) to avoid waiting for the asynchronous task to complete while prerendering. Add the [`[StreamRendering]` attribute](xref:Microsoft.AspNetCore.Components.StreamRenderingAttribute) to the `Slow` component (use `[StreamRendering(true)]` in .NET 8): ```razor @attribute [StreamRendering] ``` When the `Home` component is prerendering, the `Slow` component is quickly rendered with its loading message. The `Home` component doesn't wait for ten seconds for the `Slow` component to finish rendering. However, the finished message displayed at the end of prerendering is replaced by the loading message while the component finally renders, which is another ten-second delay. This occurs because the `Slow` component is rendering twice, along with `LoadDataAsync` executing twice. When a component is accessing resources, such as services and databases, double execution of service calls and database queries creates undesirable load on the app's resources. To address the double rendering of the loading message and the re-execution of service and database calls, persist prerendered state with for final rendering of the component, as seen in the following updates to the `Slow` component: ```razor @page "/slow" @attribute [StreamRendering] @implements IDisposable @inject PersistentComponentState ApplicationState

Slow Component

@if (data is null) {
Loading...
} else {
@data
} @code { private string? data; private PersistingComponentStateSubscription persistingSubscription; protected override async Task OnInitializedAsync() { persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData); if (!ApplicationState.TryTakeFromJson("data", out var restored)) { data = await LoadDataAsync(); } else { data = restored!; } } private Task PersistData() { ApplicationState.PersistAsJson("data", data); return Task.CompletedTask; } private async Task LoadDataAsync() { await Task.Delay(10000); return "Finished!"; } void IDisposable.Dispose() { persistingSubscription.Dispose(); } } ``` By combining streaming rendering with persistent component state: * Services and databases only require a single call for component initialization. * Components render their UIs quickly with loading messages during long-running tasks for the best user experience. For more information, see the following resources: * * . :::moniker-end :::moniker range="< aspnetcore-8.0" Quiescence during prerendering results in a poor user experience. The delay can be addressed in apps that target .NET 8 or later with a feature called *streaming rendering*, usually combined with [persisting component state during prerendering](xref:blazor/components/integration#persist-prerendered-state) to avoid waiting for the asynchronous task to complete. In versions of .NET earlier than 8.0, executing a [long-running background task](#cancelable-background-work) that loads the data after final rendering can address a long rendering delay due to quiescence. :::moniker-end ## Handle errors For information on handling errors during lifecycle method execution, see . ## Stateful reconnection after prerendering When prerendering on the server, a component is initially rendered statically as part of the page. Once the browser establishes a SignalR connection back to the server, the component is rendered *again* and interactive. If the [`OnInitialized{Async}`](#component-initialization-oninitializedasync) lifecycle method for initializing the component is present, the method is executed *twice*: * When the component is prerendered statically. * After the server connection has been established. This can result in a noticeable change in the data displayed in the UI when the component is finally rendered. To avoid this behavior, pass in an identifier to cache the state during prerendering and to retrieve the state after prerendering. The following code demonstrates a `WeatherForecastService` that avoids the change in data display due to prerendering. The awaited (`await Task.Delay(...)`) simulates a short delay before returning data from the `GetForecastAsync` method. Add services with on the service collection in the app's `Program` file: ```csharp builder.Services.AddMemoryCache(); ``` `WeatherForecastService.cs`: :::moniker range=">= aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/WeatherForecastService.cs"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="csharp" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/WeatherForecastService.cs"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="csharp" source="~/../blazor-samples/7.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="csharp" source="~/../blazor-samples/6.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="csharp" source="~/../blazor-samples/5.0/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="csharp" source="~/../blazor-samples/3.1/BlazorSample_Server/lifecycle/WeatherForecastService.cs"::: :::moniker-end For more information on the , see . :::moniker range=">= aspnetcore-8.0" The content in this section focuses on Blazor Web Apps and stateful SignalR *reconnection*. To preserve state during the execution of initialization code while prerendering, see . :::moniker-end :::moniker range="< aspnetcore-8.0" Although the content in this section focuses on Blazor Server and stateful SignalR *reconnection*, the scenario for prerendering in hosted Blazor WebAssembly solutions () involves similar conditions and approaches to prevent executing developer code twice. To preserve state during the execution of initialization code while prerendering, see . :::moniker-end ## Prerendering with JavaScript interop [!INCLUDE[](~/blazor/includes/prerendering.md)] ## Component disposal with `IDisposable` and `IAsyncDisposable` If a component implements or , the framework calls for resource disposal when the component is removed from the UI. Don't rely on the exact timing of when these methods are executed. For example, can be triggered before or after an asynchronous awaited in [`OnInitalizedAsync`](#component-initialization-oninitializedasync) is called or completes. Also, object disposal code shouldn't assume that objects created during initialization or other lifecycle methods exist. Components shouldn't need to implement and simultaneously. If both are implemented, the framework only executes the asynchronous overload. Developer code must ensure that implementations don't take a long time to complete. ### Disposal of JavaScript interop object references Examples throughout the [JavaScript (JS) interop articles](xref:blazor/js-interop/index) demonstrate typical object disposal patterns: * When calling JS from .NET, as described in , dispose any created // either from .NET or from JS to avoid leaking JS memory. * When calling .NET from JS, as described in , dispose of a created either from .NET or from JS to avoid leaking .NET memory. JS interop object references are implemented as a map keyed by an identifier on the side of the JS interop call that creates the reference. When object disposal is initiated from either the .NET or JS side, Blazor removes the entry from the map, and the object can be garbage collected as long as no other strong reference to the object is present. At a minimum, always dispose objects created on the .NET side to avoid leaking .NET managed memory. ### DOM cleanup tasks during component disposal For more information, see . For guidance on when a circuit is disconnected, see . For general JavaScript interop error handling guidance, see the *JavaScript interop* section in . ### Synchronous `IDisposable` For synchronous disposal tasks, use . The following component: * Implements with the [`@implements`](xref:mvc/views/razor#implements) Razor directive. * Disposes of `obj`, which is a type that implements . * A null check is performed because `obj` is created in a lifecycle method (not shown). ```razor @implements IDisposable ... @code { ... public void Dispose() { obj?.Dispose(); } } ``` If a single object requires disposal, a lambda can be used to dispose of the object when is called. The following example appears in the article and demonstrates the use of a lambda expression for the disposal of a . :::moniker range=">= aspnetcore-9.0" `TimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/TimerDisposal1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `TimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/TimerDisposal1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CounterWithTimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CounterWithTimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CounterWithTimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CounterWithTimerDisposal1.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal1.razor"::: :::moniker-end > [!NOTE] > In the preceding example, the call to is wrapped by a call to because the callback is invoked outside of Blazor's synchronization context. For more information, see . If the object is created in a lifecycle method, such as [`OnInitialized{Async}`](#component-initialization-oninitializedasync), check for `null` before calling `Dispose`. :::moniker range=">= aspnetcore-9.0" `TimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/TimerDisposal2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" `TimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/TimerDisposal2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" `CounterWithTimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" `CounterWithTimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" `CounterWithTimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" `CounterWithTimerDisposal2.razor`: :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/CounterWithTimerDisposal2.razor"::: :::moniker-end For more information, see: * [Cleaning up unmanaged resources (.NET documentation)](/dotnet/standard/garbage-collection/unmanaged) * [Null-conditional operators ?. and ?[]](/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-) ### Asynchronous `IAsyncDisposable` For asynchronous disposal tasks, use . The following component: * Implements with the [`@implements`](xref:mvc/views/razor#implements) Razor directive. * Disposes of `obj`, which is an unmanaged type that implements . * A null check is performed because `obj` is created in a lifecycle method (not shown). ```razor @implements IAsyncDisposable ... @code { ... public async ValueTask DisposeAsync() { if (obj is not null) { await obj.DisposeAsync(); } } } ``` For more information, see: * [Cleaning up unmanaged resources (.NET documentation)](/dotnet/standard/garbage-collection/unmanaged) * [Null-conditional operators ?. and ?[]](/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-) ### Assignment of `null` to disposed objects Usually, there's no need to assign `null` to disposed objects after calling /. Rare cases for assigning `null` include the following: * If the object's type is poorly implemented and doesn't tolerate repeat calls to /, assign `null` after disposal to gracefully skip further calls to /. * If a long-lived process continues to hold a reference to a disposed object, assigning `null` allows the [garbage collector](/dotnet/standard/garbage-collection/fundamentals) to free the object in spite of the long-lived process holding a reference to it. These are unusual scenarios. For objects that are implemented correctly and behave normally, there's no point in assigning `null` to disposed objects. In the rare cases where an object must be assigned `null`, we recommend documenting the reason and seeking a solution that prevents the need to assign `null`. ### `StateHasChanged` > [!NOTE] > Calling in `Dispose` and `DisposeAsync` isn't supported. might be invoked as part of tearing down the renderer, so requesting UI updates at that point isn't supported. ### Event handlers Always unsubscribe event handlers from .NET events. The following [Blazor form](xref:blazor/forms/index) examples show how to unsubscribe an event handler in the `Dispose` method: * Private field and lambda approach ```razor @implements IDisposable ... @code { ... private EventHandler? fieldChanged; protected override void OnInitialized() { editContext = new(model); fieldChanged = (_, __) => { ... }; editContext.OnFieldChanged += fieldChanged; } public void Dispose() { editContext.OnFieldChanged -= fieldChanged; } } ``` * Private method approach ```razor @implements IDisposable ... @code { ... protected override void OnInitialized() { editContext = new(model); editContext.OnFieldChanged += HandleFieldChanged; } private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { ... } public void Dispose() { editContext.OnFieldChanged -= HandleFieldChanged; } } ``` For more information, see the [Component disposal with `IDisposable` and `IAsyncDisposable`](#component-disposal-with-idisposable-and-iasyncdisposable) section. For more information on the component and forms, see and the other forms articles in the *Forms* node. ### Anonymous functions, methods, and expressions When [anonymous functions](/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-functions), methods, or expressions, are used, it isn't necessary to implement and unsubscribe delegates. However, failing to unsubscribe a delegate is a problem **when the object exposing the event outlives the lifetime of the component registering the delegate**. When this occurs, a memory leak results because the registered delegate keeps the original object alive. Therefore, only use the following approaches when you know that the event delegate disposes quickly. When in doubt about the lifetime of objects that require disposal, subscribe a delegate method and properly dispose the delegate as the earlier examples show. * Anonymous lambda method approach (explicit disposal not required): ```csharp private void HandleFieldChanged(object sender, FieldChangedEventArgs e) { formInvalid = !editContext.Validate(); StateHasChanged(); } protected override void OnInitialized() { editContext = new(starship); editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e); } ``` * Anonymous lambda expression approach (explicit disposal not required): ```csharp private ValidationMessageStore? messageStore; [CascadingParameter] private EditContext? CurrentEditContext { get; set; } protected override void OnInitialized() { ... messageStore = new(CurrentEditContext); CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear(); CurrentEditContext.OnFieldChanged += (s, e) => messageStore.Clear(e.FieldIdentifier); } ``` The full example of the preceding code with anonymous lambda expressions appears in the article. For more information, see [Cleaning up unmanaged resources](/dotnet/standard/garbage-collection/unmanaged) and the topics that follow it on implementing the `Dispose` and `DisposeAsync` methods. ### Disposal during JS interop Trap in potential cases where loss of Blazor's SignalR circuit prevents JS interop calls and results an unhandled exception. For more information, see the following resources: * [JavaScript isolation in JavaScript modules](xref:blazor/js-interop/call-javascript-from-dotnet#javascript-isolation-in-javascript-modules) * [JavaScript interop calls without a circuit](xref:blazor/js-interop/index#javascript-interop-calls-without-a-circuit) ## Cancelable background work Components often perform long-running background work, such as making network calls () and interacting with databases. It's desirable to stop the background work to conserve system resources in several situations. For example, background asynchronous operations don't automatically stop when a user navigates away from a component. Other reasons why background work items might require cancellation include: * An executing background task was started with faulty input data or processing parameters. * The current set of executing background work items must be replaced with a new set of work items. * The priority of currently executing tasks must be changed. * The app must be shut down for server redeployment. * Server resources become limited, necessitating the rescheduling of background work items. To implement a cancelable background work pattern in a component: * Use a and . * On [disposal of the component](#component-disposal-with-idisposable-and-iasyncdisposable) and at any point cancellation is desired by manually canceling the token, call [`CancellationTokenSource.Cancel`](xref:System.Threading.CancellationTokenSource.Cancel%2A) to signal that the background work should be cancelled. * After the asynchronous call returns, call on the token. In the following example: * `await Task.Delay(10000, cts.Token);` represents long-running asynchronous background work. * `BackgroundResourceMethod` represents a long-running background method that shouldn't start if the `Resource` is disposed before the method is called. `BackgroundWork.razor`: :::moniker range=">= aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/BackgroundWork.razor"::: :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/BackgroundWork.razor"::: :::moniker-end :::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0" :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" :::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: :::moniker-end :::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0" :::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: :::moniker-end :::moniker range="< aspnetcore-5.0" :::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/lifecycle/BackgroundWork.razor"::: :::moniker-end ## Blazor Server reconnection events The component lifecycle events covered in this article operate separately from [server-side reconnection event handlers](xref:blazor/fundamentals/signalr#reflect-the-server-side-connection-state-in-the-ui). When the SignalR connection to the client is lost, only UI updates are interrupted. UI updates are resumed when the connection is re-established. For more information on circuit handler events and configuration, see . ## Additional resources [Handle caught exceptions outside of a Razor component's lifecycle](xref:blazor/components/sync-context#handle-caught-exceptions-outside-of-a-razor-components-lifecycle)