Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,11 @@
"source_path": "aspnetcore/blazor/host-and-deploy/webassembly/integrity-check-failures.md",
"redirect_url": "/aspnet/core/blazor/host-and-deploy/webassembly/bundle-caching-and-integrity-check-failures",
"redirect_document_id": false
},
{
"source_path": "aspnetcore/blazor/performance.md",
"redirect_url": "/aspnet/core/blazor/performance/",
"redirect_document_id": false
}
]
}
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/event-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ It's often convenient to close over additional values using C# method parameters

:::moniker-end

Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see <xref:blazor/performance#avoid-recreating-delegates-for-many-repeated-elements-or-components>.
Creating a large number of event delegates in a loop may cause poor rendering performance. For more information, see <xref:blazor/performance/rendering#avoid-recreating-delegates-for-many-repeated-elements-or-components>.

Avoid using a loop variable directly in a lambda expression, such as `i` in the preceding `for` loop example. Otherwise, the same variable is used by all lambda expressions, which results in use of the same value in all lambdas. Capture the variable's value in a local variable. In the preceding example:

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1392,7 +1392,7 @@ You can factor out child components purely as a way of reusing rendering logic.
}
```

For more information, see [Reuse rendering logic](xref:blazor/performance#define-reusable-renderfragments-in-code).
For more information, see [Reuse rendering logic](xref:blazor/performance/rendering#define-reusable-renderfragments-in-code).

## Loop variables with component parameters and child content

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ If a disposable component doesn't use a <xref:System.Threading.CancellationToken

For more information on route parameters and constraints, see <xref:blazor/fundamentals/routing>.

For an example of implementing `SetParametersAsync` manually to improve performance in some scenarios, see <xref:blazor/performance#implement-setparametersasync-manually>.
For an example of implementing `SetParametersAsync` manually to improve performance in some scenarios, see <xref:blazor/performance/rendering#implement-setparametersasync-manually>.

## After component render (`OnAfterRender{Async}`)

Expand Down
4 changes: 2 additions & 2 deletions aspnetcore/blazor/components/rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Components inherited from <xref:Microsoft.AspNetCore.Components.ComponentBase> s

In most cases, <xref:Microsoft.AspNetCore.Components.ComponentBase> conventions result in the correct subset of component rerenders after an event occurs. Developers aren't usually required to provide manual logic to tell the framework which components to rerender and when to rerender them. The overall effect of the framework's conventions is that the component receiving an event rerenders itself, which recursively triggers rerendering of descendant components whose parameter values may have changed.

For more information on the performance implications of the framework's conventions and how to optimize an app's component hierarchy for rendering, see <xref:blazor/performance#optimize-rendering-speed>.
For more information on the performance implications of the framework's conventions and how to optimize an app's component hierarchy for rendering, see <xref:blazor/performance/rendering>.

::: moniker range=">= aspnetcore-8.0"

Expand Down Expand Up @@ -147,7 +147,7 @@ Even if <xref:Microsoft.AspNetCore.Components.ComponentBase.ShouldRender%2A> is

::: moniker-end

For more information on performance best practices pertaining to <xref:Microsoft.AspNetCore.Components.ComponentBase.ShouldRender%2A>, see <xref:blazor/performance#avoid-unnecessary-rendering-of-component-subtrees>.
For more information on performance best practices pertaining to <xref:Microsoft.AspNetCore.Components.ComponentBase.ShouldRender%2A>, see <xref:blazor/performance/rendering#avoid-unnecessary-rendering-of-component-subtrees>.

## `StateHasChanged`

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/components/templated-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,6 @@ Without using the `@key` directive attribute in the `TableTemplate` component, t

## Additional resources

* <xref:blazor/performance#define-reusable-renderfragments-in-code>
* <xref:blazor/performance/rendering#define-reusable-renderfragments-in-code>
* <xref:blazor/components/key>
* [Blazor samples GitHub repository (`dotnet/blazor-samples`)](https://github.com/dotnet/blazor-samples) ([how to download](xref:blazor/fundamentals/index#sample-apps))
2 changes: 1 addition & 1 deletion aspnetcore/blazor/globalization-localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Adopting [invariant globalization](#invariant-globalization) only results in usi
```

> [!NOTE]
> [`<BlazorEnableTimeZoneSupport>`](xref:blazor/performance#disable-unused-features) overrides an earlier `<InvariantTimezone>` setting. We recommend removing the `<BlazorEnableTimeZoneSupport>` setting.
> [`<BlazorEnableTimeZoneSupport>`](xref:blazor/performance/app-download-size#disable-unused-features) overrides an earlier `<InvariantTimezone>` setting. We recommend removing the `<BlazorEnableTimeZoneSupport>` setting.

:::moniker-end

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/host-and-deploy/configure-linker.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,4 @@ For more information, see [I18N: Pnetlib Internationalization Framework Library

## Additional resources

* <xref:blazor/performance#intermediate-language-il-linking>
<xref:blazor/performance/app-download-size#intermediate-language-il-linking>
2 changes: 1 addition & 1 deletion aspnetcore/blazor/host-and-deploy/configure-trimmer.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,4 @@ Because custom types are never trimmed by Blazor when an app is published, the c
## Additional resources

* [Trim self-contained deployments and executables](/dotnet/core/deploying/trimming/trim-self-contained)
* <xref:blazor/performance#intermediate-language-il-trimming>
* <xref:blazor/performance/app-download-size#intermediate-language-il-trimming>
1 change: 1 addition & 0 deletions aspnetcore/blazor/javascript-interoperability/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Further JS interop guidance is provided in the following articles:

* <xref:blazor/js-interop/call-javascript-from-dotnet>
* <xref:blazor/js-interop/call-dotnet-from-javascript>
* <xref:blazor/performance/js-interop>

:::moniker range=">= aspnetcore-7.0"

Expand Down
118 changes: 118 additions & 0 deletions aspnetcore/blazor/performance/app-download-size.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
title: ASP.NET Core Blazor app download size performance best practices
author: guardrex
description: Tips for reducing app download size in ASP.NET Core Blazor apps and avoiding common performance problems.
monikerRange: '>= aspnetcore-3.1'
ms.author: riande
ms.custom: mvc
ms.date: 04/30/2025
uid: blazor/performance/app-download-size
---
# ASP.NET Core Blazor app download size performance best practices

[!INCLUDE[](~/includes/not-latest-version.md)]

:::moniker range=">= aspnetcore-6.0"

## Runtime relinking

For information on how runtime relinking minimizes an app's download size, see <xref:blazor/tooling/webassembly#runtime-relinking>.

:::moniker-end

## Use `System.Text.Json`

Blazor's JS interop implementation relies on <xref:System.Text.Json>, which is a high-performance JSON serialization library with low memory allocation. Using <xref:System.Text.Json> shouldn't result in additional app payload size over adding one or more alternate JSON libraries.

For migration guidance, see [How to migrate from `Newtonsoft.Json` to `System.Text.Json`](/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to).

## Intermediate Language (IL) trimming

*This section only applies to client-side Blazor scenarios.*

:::moniker range=">= aspnetcore-5.0"

Trimming unused assemblies from a Blazor WebAssembly app reduces the app's size by removing unused code in the app's binaries. For more information, see <xref:blazor/host-and-deploy/configure-trimmer>.

:::moniker-end

:::moniker range="< aspnetcore-5.0"

[Linking a Blazor WebAssembly app](xref:blazor/host-and-deploy/configure-linker) reduces the app's size by trimming unused code in the app's binaries. The Intermediate Language (IL) Linker is only enabled when building in `Release` configuration. To benefit from this, publish the app for deployment using the [`dotnet publish`](/dotnet/core/tools/dotnet-publish) command with the [-c|--configuration](/dotnet/core/tools/dotnet-publish#options) option set to `Release`:

```dotnetcli
dotnet publish -c Release
```

:::moniker-end

## Lazy load assemblies

*This section only applies to client-side Blazor scenarios.*

Load assemblies at runtime when the assemblies are required by a route. For more information, see <xref:blazor/webassembly-lazy-load-assemblies>.

## Compression

*This section only applies to Blazor WebAssembly apps.*

When a Blazor WebAssembly app is published, the output is statically compressed during publish to reduce the app's size and remove the overhead for runtime compression. Blazor relies on the server to perform content negotiation and serve statically-compressed files.

After an app is deployed, verify that the app serves compressed files. Inspect the **Network** tab in a browser's [developer tools](https://developer.mozilla.org/docs/Glossary/Developer_Tools) and verify that the files are served with `Content-Encoding: br` (Brotli compression) or `Content-Encoding: gz` (Gzip compression). If the host isn't serving compressed files, follow the instructions in <xref:blazor/host-and-deploy/webassembly/index#compression>.

## Disable unused features

*This section only applies to client-side Blazor scenarios.*

Blazor WebAssembly's runtime includes the following .NET features that can be disabled for a smaller payload size.

Blazor WebAssembly carries globalization resources required to display values, such as dates and currency, in the user's culture. If the app doesn't require localization, you may configure the app to [support the invariant culture](xref:blazor/globalization-localization#invariant-globalization), which is based on the `en-US` culture. Apply the `<InvariantGlobalization>` MSBuild property with a value of `true` in the app's project file (`.csproj`):

```xml
<PropertyGroup>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
```

:::moniker range=">= aspnetcore-8.0"

Adopting [invariant globalization](xref:blazor/globalization-localization#invariant-globalization) only results in using non-localized timezone names. To trim timezone code and data from the app, apply the `<InvariantTimezone>` MSBuild property with a value of `true` in the app's project file (`.csproj`):

```xml
<PropertyGroup>
<InvariantTimezone>true</InvariantTimezone>
</PropertyGroup>
```

> [!NOTE]
> [`<BlazorEnableTimeZoneSupport>`](xref:blazor/performance/app-download-size#disable-unused-features) overrides an earlier `<InvariantTimezone>` setting. We recommend removing the `<BlazorEnableTimeZoneSupport>` setting.

:::moniker-end

:::moniker range="< aspnetcore-8.0"

A data file is included to make timezone information correct. If the app doesn't require this feature, consider disabling it by setting the `<BlazorEnableTimeZoneSupport>` MSBuild property to `false` in the app's project file:

```xml
<PropertyGroup>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
</PropertyGroup>
```

:::moniker-end

:::moniker range="< aspnetcore-5.0"

Collation information is included to make APIs such as <xref:System.StringComparison.InvariantCultureIgnoreCase?displayProperty=nameWithType> work correctly. If you're certain that the app doesn't require the collation data, consider disabling it by setting the `BlazorWebAssemblyPreserveCollationData` MSBuild property in the app's project file to `false`:

```xml
<PropertyGroup>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
</PropertyGroup>
```

:::moniker-end

## Additional resources

[Configuring and hosting .NET WebAssembly applications](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/features.md)
Loading