Skip to content

Latest commit

 

History

History
411 lines (247 loc) · 19 KB

File metadata and controls

411 lines (247 loc) · 19 KB
title author description monikerRange ms.author ms.custom ms.date uid
ASP.NET Core Blazor layouts
guardrex
Learn how to create reusable layout components for Blazor apps.
>= aspnetcore-3.1
riande
mvc
03/13/2023
blazor/components/layouts

ASP.NET Core Blazor layouts

[!INCLUDE]

This article explains how to create reusable layout components for Blazor apps.

Some app elements, such as menus, copyright messages, and company logos, are usually part of app's overall presentation. Placing a copy of the markup for these elements into all of the components of an app isn't efficient. Every time that one of these elements is updated, every component that uses the element must be updated. This approach is costly to maintain and can lead to inconsistent content if an update is missed. Layouts solve these problems.

A Blazor layout is a Razor component that shares markup with components that reference it. Layouts can use data binding, dependency injection, and other features of components.

Layout components

Create a layout component

To create a layout component:

  • Create a Razor component defined by a Razor template or C# code. Layout components based on a Razor template use the .razor file extension just like ordinary Razor components. Because layout components are shared across an app's components, they're usually placed in the app's shared or layout folder. However, layouts can be placed in any location accessible to the components that use it. For example, a layout can be placed in the same folder as the components that use it.
  • Inherit the component from xref:Microsoft.AspNetCore.Components.LayoutComponentBase. The xref:Microsoft.AspNetCore.Components.LayoutComponentBase defines a xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body property (xref:Microsoft.AspNetCore.Components.RenderFragment type) for the rendered content inside the layout.
  • Use the Razor syntax @Body to specify the location in the layout markup where the content is rendered.

Note

For more information on xref:Microsoft.AspNetCore.Components.RenderFragment, see xref:blazor/components/index#child-content-render-fragments.

The following DoctorWhoLayout component shows the Razor template of a layout component. The layout inherits xref:Microsoft.AspNetCore.Components.LayoutComponentBase and sets the @Body between the navigation bar (<nav>...</nav>) and the footer (<footer>...</footer>).

DoctorWhoLayout.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout.razor" highlight="1,13":::

:::moniker-end

MainLayout component

In an app created from a Blazor project template, the MainLayout component is the app's default layout. Blazor's layout adopts the :::no-loc text="Flexbox"::: layout model (MDN documentation) (W3C specification).

MainLayout.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/MainLayout.razor":::

:::moniker-end

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

Blazor's CSS isolation feature applies isolated CSS styles to the MainLayout component. By convention, the styles are provided by the accompanying stylesheet of the same name, MainLayout.razor.css. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (dotnet/aspnetcore GitHub repository):

[!INCLUDE]

:::moniker-end

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

Blazor's CSS isolation feature applies isolated CSS styles to the MainLayout component. By convention, the styles are provided by the accompanying stylesheet of the same name, MainLayout.razor.css. The ASP.NET Core framework implementation of the stylesheet is available for inspection in the ASP.NET Core reference source (dotnet/aspnetcore GitHub repository):

[!INCLUDE]

:::moniker-end

Apply a layout

Apply a layout to a component

Use the @layout Razor directive to apply a layout to a routable Razor component that has an @page directive. The compiler converts @layout into a xref:Microsoft.AspNetCore.Components.LayoutAttribute and applies the attribute to the component class.

The content of the following Episodes component is inserted into the DoctorWhoLayout at the position of @Body.

Episodes.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/layouts/Episodes.razor" highlight="2":::

:::moniker-end

The following rendered HTML markup is produced by the preceding DoctorWhoLayout and Episodes component. Extraneous markup doesn't appear in order to focus on the content provided by the two components involved:

  • The Doctor Who™ Episode Database heading (<h1>...</h1>) in the header (<header>...</header>), navigation bar (<nav>...</nav>), and trademark information element (<div>...</div>) in the footer (<footer>...</footer>) come from the DoctorWhoLayout component.
  • The Episodes heading (<h2>...</h2>) and episode list (<ul>...</ul>) come from the Episodes component.
<body>
    <div id="app">
        <header>
            <h1>Doctor Who&trade; Episode Database</h1>
        </header>

        <nav>
            <a href="main-list">Main Episode List</a>
            <a href="search">Search</a>
            <a href="new">Add Episode</a>
        </nav>

        <h2>Episodes</h2>

        <ul>
            <li>...</li>
            <li>...</li>
            <li>...</li>
        </ul>

        <footer>
            Doctor Who is a registered trademark of the BBC. 
            https://www.doctorwho.tv/
        </footer>
    </div>
</body>

Specifying the layout directly in a component overrides a default layout:

Apply a layout to a folder of components

Every folder of an app can optionally contain a template file named _Imports.razor. The compiler includes the directives specified in the imports file in all of the Razor templates in the same folder and recursively in all of its subfolders. Therefore, an _Imports.razor file containing @layout DoctorWhoLayout ensures that all of the components in a folder use the DoctorWhoLayout component. There's no need to repeatedly add @layout DoctorWhoLayout to all of the Razor components (.razor) within the folder and subfolders.

_Imports.razor:

@layout DoctorWhoLayout
...

The _Imports.razor file is similar to the _ViewImports.cshtml file for Razor views and pages but applied specifically to Razor component files.

Specifying a layout in _Imports.razor overrides a layout specified as the router's default app layout, which is described in the following section.

Warning

Do not add a Razor @layout directive to the root _Imports.razor file, which results in an infinite loop of layouts. To control the default app layout, specify the layout in the Router component. For more information, see the following Apply a default layout to an app section.

Note

The @layout Razor directive only applies a layout to routable Razor components with an @page directive.

Apply a default layout to an app

Specify the default app layout in the App component's xref:Microsoft.AspNetCore.Components.Routing.Router component. The following example from an app based on a Blazor project template sets the default layout to the MainLayout component.

App.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3":::

[!INCLUDE]

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/layouts/App1.razor" highlight="3":::

:::moniker-end

For more information on the xref:Microsoft.AspNetCore.Components.Routing.Router component, see xref:blazor/fundamentals/routing.

Specifying the layout as a default layout in the Router component is a useful practice because you can override the layout on a per-component or per-folder basis, as described in the preceding sections of this article. We recommend using the Router component to set the app's default layout because it's the most general and flexible approach for using layouts.

Apply a layout to arbitrary content (LayoutView component)

To set a layout for arbitrary Razor template content, specify the layout with a xref:Microsoft.AspNetCore.Components.LayoutView component. You can use a xref:Microsoft.AspNetCore.Components.LayoutView in any Razor component. The following example sets a layout component named ErrorLayout for the MainLayout component's xref:Microsoft.AspNetCore.Components.Routing.Router.NotFound template (<NotFound>...</NotFound>).

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

Note

The following example is specifically for a Blazor WebAssembly app because Blazor Web Apps don't use the xref:Microsoft.AspNetCore.Components.Routing.Router.NotFound template (<NotFound>...</NotFound>). However, the template is supported for backward compatibility to avoid a breaking change in the framework. Blazor Web Apps typically process bad URL requests by either displaying the browser's built-in 404 UI or returning a custom 404 page from the ASP.NET Core server via ASP.NET Core middleware (for example, UseStatusCodePagesWithRedirects / API documentation).

:::moniker-end

App.razor:

<Router ...>
    <Found ...>
        ...
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(ErrorLayout)">
            <h1>Page not found</h1>
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

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

[!INCLUDE]

:::moniker-end

Nested layouts

A component can reference a layout that in turn references another layout. For example, nested layouts are used to create a multi-level menu structures.

The following example shows how to use nested layouts. The Episodes component shown in the Apply a layout to a component section is the component to display. The component references the DoctorWhoLayout component.

The following DoctorWhoLayout component is a modified version of the example shown earlier in this article. The header and footer elements are removed, and the layout references another layout, ProductionsLayout. The Episodes component is rendered where @Body appears in the DoctorWhoLayout.

DoctorWhoLayout.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/DoctorWhoLayout2.razor" highlight="2,12":::

:::moniker-end

The ProductionsLayout component contains the top-level layout elements, where the header (<header>...</header>) and footer (<footer>...</footer>) elements now reside. The DoctorWhoLayout with the Episodes component is rendered where @Body appears.

ProductionsLayout.razor:

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

:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13":::

:::moniker-end

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

:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/layouts/ProductionsLayout.razor" highlight="13":::

:::moniker-end

The following rendered HTML markup is produced by the preceding nested layout. Extraneous markup doesn't appear in order to focus on the nested content provided by the three components involved:

  • The header (<header>...</header>), production navigation bar (<nav>...</nav>), and footer (<footer>...</footer>) elements and their content come from the ProductionsLayout component.
  • The Doctor Who™ Episode Database heading (<h1>...</h1>), episode navigation bar (<nav>...</nav>), and trademark information element (<div>...</div>) come from the DoctorWhoLayout component.
  • The Episodes heading (<h2>...</h2>) and episode list (<ul>...</ul>) come from the Episodes component.
<body>
    <div id="app">
        <header>
            <h1>Productions</h1>
        </header>

        <nav>
            <a href="main-production-list">Main Production List</a>
            <a href="production-search">Search</a>
            <a href="new-production">Add Production</a>
        </nav>

        <h1>Doctor Who&trade; Episode Database</h1>

        <nav>
            <a href="main-episode-list">Main Episode List</a>
            <a href="episode-search">Search</a>
            <a href="new-episode">Add Episode</a>
        </nav>

        <h2>Episodes</h2>

        <ul>
            <li>...</li>
            <li>...</li>
            <li>...</li>
        </ul>

        <div>
            Doctor Who is a registered trademark of the BBC. 
            https://www.doctorwho.tv/
        </div>

        <footer>
            Footer of Productions Layout
        </footer>
    </div>
</body>

Share a Razor Pages layout with integrated components

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

When routable components are integrated into a Razor Pages app, the app's shared layout can be used with the components. For more information, see xref:blazor/components/integration.

:::moniker-end

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

When routable components are integrated into a Razor Pages app, the app's shared layout can be used with the components. For more information, see xref:blazor/components/prerendering-and-integration.

:::moniker-end

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

Sections

To control the content in a layout from a child Razor component, see xref:blazor/components/sections.

:::moniker-end

Additional resources