Skip to content

Merge to Live #35735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from all 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: 3 additions & 2 deletions aspnetcore/blazor/components/prerender.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ In the following example that serializes state for multiple components of the sa
In the following example that serializes state for a dependency injection service:

* Properties annotated with the `[SupplyParameterFromPersistentComponentState]` attribute are serialized during prerendering and deserialized when the app becomes interactive.
* The `AddPersistentService` method is used to register the service for persistence. The render mode is required because the render mode can't be inferred from the service type. Use any of the following values:
* The <xref:Microsoft.Extensions.DependencyInjection.RazorComponentsRazorComponentBuilderExtensions.RegisterPersistentService%2A> extension method is used to register the service for persistence. The render mode is required because the render mode can't be inferred from the service type. Use any of the following values:
* `RenderMode.Server`: The service is available for the Interactive Server render mode.
* `RenderMode.Webassembly`: The service is available for the Interactive Webassembly render mode.
* `RenderMode.InteractiveAuto`: The service is available for both the Interactive Server and Interactive Webassembly render modes if a component renders in either of those modes.
Expand Down Expand Up @@ -231,7 +231,8 @@ public class CounterService
In `Program.cs`:

```csharp
builder.Services.AddPersistentService<CounterService>(RenderMode.InteractiveAuto);
builder.Services.RegisterPersistentService<CounterService>(
RenderMode.InteractiveAuto);
```

Serialized properties are identified from the actual service instance:
Expand Down
9 changes: 3 additions & 6 deletions aspnetcore/blazor/components/quickgrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,9 @@ dotnet aspnet-codegenerator blazor -h

For an example use of the QuickGrid scaffolder, see <xref:blazor/tutorials/movie-database-app/index>.

<!-- UPDATE 10.0 - PU work tracked by https://github.com/dotnet/aspnetcore/issues/58716.
Versioning out at 10.0 for now. -->

:::moniker range="< aspnetcore-10.0"
<!-- UPDATE 11.0 - PU work tracked by https://github.com/dotnet/aspnetcore/issues/58716.
We will continue to show this for now. The PU plans to look at it
for framework updates at 11.0. -->

## Multiple concurrent EF Core queries trigger `System.InvalidOperationException`

Expand Down Expand Up @@ -690,5 +689,3 @@ Consider the following example, which is based on the movie database `Index` com
}
}
```

:::moniker-end
26 changes: 26 additions & 0 deletions aspnetcore/blazor/components/rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,32 @@ Streaming rendering requires the server to avoid buffering the output. The respo

To stream content updates when using static server-side rendering (static SSR) or prerendering, apply the [`[StreamRendering]` attribute](xref:Microsoft.AspNetCore.Components.StreamRenderingAttribute) in .NET 9 or later (use `[StreamRendering(true)]` in .NET 8) to the component. Streaming rendering must be explicitly enabled because streamed updates may cause content on the page to shift. Components without the attribute automatically adopt streaming rendering if the parent component uses the feature. Pass `false` to the attribute in a child component to disable the feature at that point and further down the component subtree. The attribute is functional when applied to components supplied by a [Razor class library](xref:blazor/components/class-libraries).

:::moniker-end

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

If [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) is active, streaming rendering renders [Not Found responses](xref:blazor/fundamentals/routing#not-found-responses) without reloading the page. When enhanced navigation is blocked, the framework redirects to Not Found content with a page refresh.

Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](xref:blazor/fundamentals/routing#not-found-responses) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (<xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>). The Not Found render fragment (`<NotFound>...</NotFound>`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering.

Streaming `NavigationManager.NotFound` content rendering uses (in order):

* A `NotFoundPage` passed to the `Router` component, if present.
* A Status Code Pages Re-execution Middleware page, if configured.
* No action if neither of the preceding approaches is adopted.

Non-streaming `NavigationManager.NotFound` content rendering uses (in order):

* A `NotFoundPage` passed to the `Router` component, if present.
* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.*
* `DefaultNotFound` 404 content ("`Not found`" plain text).

[Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> takes precedence for browser-based address routing problems, such as an incorrect URL typed into the browser's address bar or selecting a link that has no endpoint in the app.

:::moniker-end

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

The following example is based on the `Weather` component in an app created from the [Blazor Web App project template](xref:blazor/project-structure#blazor-web-app). The call to <xref:System.Threading.Tasks.Task.Delay%2A?displayProperty=nameWithType> simulates retrieving weather data asynchronously. The component initially renders placeholder content ("`Loading...`") without waiting for the asynchronous delay to complete. When the asynchronous delay completes and the weather data content is generated, the content is streamed to the response and patched into the weather forecast table.

`Weather.razor`:
Expand Down
98 changes: 97 additions & 1 deletion aspnetcore/blazor/forms/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1557,12 +1557,108 @@ The <xref:System.ComponentModel.DataAnnotations.CompareAttribute> doesn't work w

:::moniker-end

## Nested models, collection types, and complex types
:::moniker range=">= aspnetcore-10.0"

## Nested objects and collection types

Blazor form validation includes support for validating properties of nested objects and collection items with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>.

To create a validated form, use a <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component inside an <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component, just as before.

To opt into the nested objects and collection types validation feature:

<!-- UPDATE 10.0 - API cross-links -->

1. Call the `AddValidation` extension method in the `Program` file where services are registered.
2. Declare the form model types in a C# class file, not in a Razor component (`.razor`).
3. Annotate the root form model type with the `[ValidatableType]` attribute.

Without following the preceding steps, form validation behavior doesn't include nested model and collection type validation.

<!-- UPDATE 10.0 - Replace with a fully working, cut-'n-paste example -->

The following example demonstrates customer orders with the improved form validation (details omitted for brevity):

In `Program.cs`, call `AddValidation` on the service collection:

```csharp
builder.Services.AddValidation();
```

In the following `Order` class, the `[ValidatableType]` attribute is required on the top-level model type. The other types are discovered automatically. `OrderItem` and `ShippingAddress` aren't shown for brevity, but nested and collection validation works the same way in those types if they were shown.

`Order.cs`:

```csharp
[ValidatableType]
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}

public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }

[Required(ErrorMessage = "Email is required.")]
public string? Email { get; set; }

public ShippingAddress ShippingAddress { get; set; } = new();
}
```

In the following `OrderPage` component, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> component is present in the <xref:Microsoft.AspNetCore.Components.Forms.EditForm> component.

`OrderPage.razor`:

```razor
<EditForm Model="Model">
<DataAnnotationsValidator />

<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>

// ... form continues ...
</EditForm>

@code {
public Order? Model { get; set; }

protected override void OnInitialized() => Model ??= new();
}
```

The requirement to declare the model types outside of Razor components (`.razor` files) is due to the fact that both the new validation feature and the Razor compiler itself are using a source generator. Currently, output of one source generator can't be used as an input for another source generator.

## Complex types

Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't complex-type properties.

To validate the bound model's entire object graph, including complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental* [`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package.

> [!NOTE]
> The `ObjectGraphDataAnnotationsValidator` isn't compatible with [nested objects and collection types validation](#nested-objects-and-collection-types), but it's capable of validating nested objects and collection types on its own.

:::moniker-end

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

## Nested objects, collection types, and complex types

Blazor provides support for validating form input using data annotations with the built-in <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator>. However, the <xref:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator> only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.

To validate the bound model's entire object graph, including collection- and complex-type properties, use the `ObjectGraphDataAnnotationsValidator` provided by the *experimental* [`Microsoft.AspNetCore.Components.DataAnnotations.Validation`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.DataAnnotations.Validation) package:

:::moniker-end

```razor
<EditForm ...>
<ObjectGraphDataAnnotationsValidator />
Expand Down
29 changes: 24 additions & 5 deletions aspnetcore/blazor/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,14 +708,33 @@ For more information on component disposal, see <xref:blazor/components/componen

<!-- UPDATE 10.0 - API doc cross-links -->

*In .NET 10 Preview 4, Not Found responses are only available for static SSR and global interactive rendering. Per-page/component rendering support is planned for Preview 5 in June, 2025.*

<xref:Microsoft.AspNetCore.Components.NavigationManager> provides a `NotFound` method to handle scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering:

* **Static SSR**: Calling `NotFound` sets the HTTP status code to 404.
* **Streaming rendering**: Throws an exception if the response has already started.

* **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing#route-templates)) to render Not Found content.

* **Streaming rendering**: If [enhanced navigation](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) is active, [streaming rendering](xref:blazor/components/rendering#streaming-rendering) renders Not Found content without reloading the page. When enhanced navigation is blocked, the framework redirects to Not Found content with a page refresh.

> [!NOTE]
> The following discussion mentions that a Not Found Razor component can be assigned to the `Router` component's `NotFoundPage` parameter. The parameter works in concert with `NavigationManager.NotFound` and is described in more detail later in this section.

Streaming rendering can only render components that have a route, such as a Not Found page assignment with the `Router` component's `NotFoundPage` parameter or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (<xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>). The Not Found render fragment (`<NotFound>...</NotFound>`) and the `DefaultNotFound` 404 content ("`Not found`" plain text) don't have routes, so they can't be used during streaming rendering.

Streaming `NavigationManager.NotFound` content rendering uses (in order):

* A `NotFoundPage` passed to the `Router` component, if present.
* A Status Code Pages Re-execution Middleware page, if configured.
* No action if neither of the preceding approaches is adopted.

Non-streaming `NavigationManager.NotFound` content rendering uses (in order):

* A `NotFoundPage` passed to the `Router` component, if present.
* Not Found render fragment content, if present. *Not recommended in .NET 10 or later.*
* `DefaultNotFound` 404 content ("`Not found`" plain text).

[Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> takes precedence for browser-based address routing problems, such as an incorrect URL typed into the browser's address bar or selecting a link that has no endpoint in the app.

When a component is rendered statically (static SSR) and `NavigationManager.NotFound` is called, the 404 status code is set on the response:

```razor
Expand Down Expand Up @@ -743,7 +762,7 @@ To provide Not Found content for global interactive rendering, use a Not Found p
<p>Sorry! Nothing to show.</p>
```

Assign the `NotFound` component to the router's `NotFoundPage` parameter. `NotFoundPage` supports routing that can be used across re-execution middleware, including non-Blazor middleware. If the `NotFound` render fragment is defined together with `NotFoundPage`, the page has higher priority.
Assign the `NotFound` component to the router's `NotFoundPage` parameter. `NotFoundPage` supports routing that can be used across Status Code Pages Re-execution Middleware, including non-Blazor middleware. If the `NotFound` render fragment (`<NotFound>...</NotFound>`) is defined together with `NotFoundPage`, the page has higher priority.

In the following example, the preceding `NotFound` component is present in the app's `Pages` folder and passed to the `NotFoundPage` parameter:

Expand Down Expand Up @@ -779,7 +798,7 @@ You can use the `OnNotFound` event for notifications when `NotFound` is invoked.

<!-- UPDATE 10.0 - For Pre5, the following can be expanded to
cover CSR with an added bit of coverage for
re-execution middleware. -->
Re-execution Middleware. -->

In the following example for components that adopt [interactive server-side rendering (interactive SSR)](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), custom content is rendered depending on where `OnNotFound` is called. If the event is triggered by the following `Movie` component when a movie isn't found on component initialization, a custom message states that the requested movie isn't found. If the event is triggered by the `User` component, a different message states that the user isn't found.

Expand Down
34 changes: 34 additions & 0 deletions aspnetcore/blazor/host-and-deploy/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,40 @@ For more information on *solutions*, see <xref:blazor/tooling#visual-studio-solu

:::moniker-end

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

## JavaScript bundler support

The Blazor runtime relies on JavaScript (JS) files, the .NET runtime compiled into WebAssembly code, and managed assemblies packed as WebAssembly files. When a Blazor app is built, the Blazor runtime depends on these files from different build locations. Due to this constraint, Blazor's build output isn't compatible with JS bundlers, such as [Gulp](https://gulpjs.com), [Webpack](https://webpack.js.org), and [Rollup](https://rollupjs.org/).

To produce build output compatible with JS bundlers *during publish*, set the `WasmBundlerFriendlyBootConfig` MSBuild property to `true` in the app's project file:

```xml
<WasmBundlerFriendlyBootConfig>true</WasmBundlerFriendlyBootConfig>
```

> [!IMPORTANT]
> This feature only produces the bundler-friendly output when publishing the app.

The output isn't directly runnable in the browser, but it can be consumed by JS tools to bundle JS files with the rest of the developer-supplied scripts.

When `WasmBundlerFriendlyBootConfig` is enabled, the produced JS contains `import` directives for all of the assets in the app, which makes the dependencies visible for the bundler. Many of the assets aren't loadable by the browser, but bundlers usually can be configured to recognize the assets by their file type to handle loading. For details on how to configure your bundler, refer to the bundler's documentation.

> [!NOTE]
> Bundling build output should be possible by mapping imports to individual file locations using a JS bundler custom plugin. We don't provide such a plugin at the moment.

> [!NOTE]
> Replacing the `files` plugin with `url`, all of the app's JS files, including the Blazor-WebAssembly runtime (base64 encoded in the JS), are bundled into the output. The size of the file is significantly larger (for example, 300% larger) than when the files are curated with the `files` plugin, so we don't recommend using the `url` plugin as a general practice when producing bundler-friendly output for JS bundler processing.

The following sample apps are based on [Rollup](https://rollupjs.org/). Similar concepts apply when using other JS bundlers.

Demonstration sample apps:

* [Blazor WASM in a React application (`maraf/blazor-wasm-react` GitHub repository)](https://github.com/maraf/blazor-wasm-react)
* [.NET on WASM in a React component sample (`maraf/dotnet-wasm-react` GitHub repository)](https://github.com/maraf/dotnet-wasm-react)

:::moniker-end

## Blazor Server `MapFallbackToPage` configuration

*This section only applies to Blazor Server apps. <xref:Microsoft.AspNetCore.Builder.RazorPagesEndpointRouteBuilderExtensions.MapFallbackToPage%2A> isn't supported in Blazor Web Apps and Blazor WebAssembly apps.*
Expand Down
Loading
Loading