Configure the Trimmer for ASP.NET Core Blazor

Note

This isn't the latest version of this article. For the current release, see the .NET 9 version of this article.

Warning

This version of ASP.NET Core is no longer supported. For more information, see the .NET and .NET Core Support Policy. For the current release, see the .NET 9 version of this article.

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 9 version of this article.

This article explains how to control the Intermediate Language (IL) Trimmer when building a Blazor app.

Blazor WebAssembly performs Intermediate Language (IL) trimming to reduce the size of the published output. Trimming occurs when publishing an app.

Configuration

To configure the IL Trimmer, see the Trimming options article in the .NET Fundamentals documentation, which includes guidance on the following subjects:

  • Disable trimming for the entire app with the <PublishTrimmed> property in the project file.
  • Control how aggressively unused IL is discarded by the IL Trimmer.
  • Stop the IL Trimmer from trimming specific assemblies.
  • "Root" assemblies for trimming.
  • Surface warnings for reflected types by setting the <SuppressTrimAnalysisWarnings> property to false in the project file.
  • Control symbol trimming and debugger support.
  • Set IL Trimmer features for trimming framework library features.

Default trimmer granularity

The default trimmer granularity for Blazor apps is partial. To trim all assemblies, change the granularity to full in the app's project file:

<ItemGroup>
  <TrimMode>full</TrimMode>
</ItemGroup>

For more information, see Trimming options (.NET documentation).

Failure to preserve types used by a published app

Trimming may have detrimental effects for a published app leading to runtime errors. In apps that use reflection, the IL Trimmer often can't determine the required types for runtime reflection and trims them away or trims away parameter names from methods. This can happen with complex framework types used for JS interop, JSON serialization/deserialization, and other operations.

The IL Trimmer is also unable to react to an app's dynamic behavior at runtime. To ensure the trimmed app works correctly once deployed, test published output frequently while developing.

Consider the following client-side component in a Blazor Web App (ASP.NET Core 8.0 or later) that deserializes a KeyValuePair collection (List<KeyValuePair<string, string>>):

@rendermode @(new InteractiveWebAssemblyRenderMode(false))
@using System.Diagnostics.CodeAnalysis
@using System.Text.Json

<dl>
    @foreach (var item in @items)
    {
        <dt>@item.Key</dt>
        <dd>@item.Value</dd>
    }
</dl>

@code {
    private List<KeyValuePair<string, string>> items = [];

    [StringSyntax(StringSyntaxAttribute.Json)]
    private const string data =
        """[{"key":"key 1","value":"value 1"},{"key":"key 2","value":"value 2"}]""";

    protected override void OnInitialized()
    {
        JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };

        items = JsonSerializer
            .Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
    }
}

The preceding component executes normally when the app is run locally and produces the following rendered definition list (<dl>):

key 1
value 1
key 2
value 2

When the app is published, KeyValuePair is trimmed from the app, even in spite of setting the <PublishTrimmed> property to false in the project file. Accessing the component throws the following exception:

Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]

To address lost types, consider the following approaches.

Preserve the type as a dynamic dependency

We recommend creating a dynamic dependency to preserve the type with the [DynamicDependency] attribute.

If not already present, add an @using directive for System.Diagnostics.CodeAnalysis:

@using System.Diagnostics.CodeAnalysis

Add a [DynamicDependency] attribute to preserve the KeyValuePair:

+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(KeyValuePair<string, string>))]
private List<KeyValuePair<string, string>> items = [];

Custom types

The following modifications create a StringKeyValuePair type for use by the component.

StringKeyValuePair.cs:

[method: SetsRequiredMembers]
public sealed class StringKeyValuePair(string key, string value)
{
    public required string Key { get; init; } = key;
    public required string Value { get; init; } = value;
}

The component is modified to use the StringKeyValuePair type:

- private List<KeyValuePair<string, string>> items = [];
+ private List<StringKeyValuePair> items = [];
- items = JsonSerializer.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
+ items = JsonSerializer.Deserialize<List<StringKeyValuePair>>(data, options)!;

Because custom types are never trimmed by Blazor when an app is published, the component works as designed after the app is published.

Additional resources