Layouts

A layout wraps the routed page with shared chrome: headers, navigation, footers. BlazorCodeFirst layouts are written the same way as components: derive from ChromeLayoutBase, declare a design-time UI expression, and let the source generator produce the rendering.

Chrome and Body#

ChromeLayoutBase derives from Blazor's LayoutComponentBase, so it already has a Body parameter holding the routed page. The chrome the layout itself draws goes in a separate overridden property, Chrome:

using BlazorCodeFirst;
using static BlazorCodeFirst.Html;

public partial class MainLayout : ChromeLayoutBase
{
    protected override View Chrome =>
        Div.Class("shell")[
            Header[H1["My App"]],
            Main.Class("content")[Body],
            Footer["© 2026"]];
}

Main[Body] here is exactly <main>@Body</main> in Razor, the routed page placed as element content. The output below shows a placeholder where the routed page appears:

protected override View Chrome =>
    Div.Class("shell")[
        Header[H1["My App"]],
        Main.Class("content")[Body],
        Footer["© 2026"]];
<div class="shell">
    <header><h1>My App</h1></header>
    <main class="content">the routed page</main>
    <footer>© 2026</footer>
</div>

Why Chrome, not Body#

Blazor requires a layout's routed content to be exposed through a parameter named exactly Body, and C# cannot declare two members with the same name on one type. So Body keeps its Razor meaning, the page being wrapped, and the layout's own design-time expression is named Chrome instead.

Nesting layouts#

A layout can itself sit inside another layout. Put [Layout] on the layout type, exactly as you would on a page:

using BlazorCodeFirst;
using Microsoft.AspNetCore.Components;
using static BlazorCodeFirst.Html;

[Layout(typeof(SiteLayout))]
public partial class DocsLayout : ChromeLayoutBase
{
    protected override View Chrome => Div.Class("docs")[Aside[TableOfContents()], Main[Body]];
}

Nesting is resolved by Blazor, not by BlazorCodeFirst: LayoutView reads the attribute off the layout type and wraps it in its own layout, and a BlazorCodeFirst layout is an ordinary LayoutComponentBase descendant. Each level's Body holds the level below it. SiteLayout's Body is the rendered DocsLayout, whose own Body is the routed page.

RenderFragment becomes content directly#

Body is a plain Blazor RenderFragment?, not a BlazorCodeFirst type, yet Main[Body] above compiles without dedicated syntax. View has an implicit conversion from RenderFragment?, so any fragment can appear wherever element content is expected. The conversion is from the non-generic RenderFragment only, and a RenderFragment<T> does not convert. Like Fragment and Raw, a RenderFragment opens no keyable frame, so it cannot be a ForEach content root (BCF3003) and cannot carry decorations (BCF3008).

The same mechanism lets a BlazorCodeFirst component render children passed in from Razor. A component with [Parameter] public RenderFragment? ChildContent uses it exactly like Body:

using BlazorCodeFirst;
using Microsoft.AspNetCore.Components;
using static BlazorCodeFirst.Html;

public partial class Card : BodyComponentBase
{
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    protected override View Body => Div.Class("card")[ChildContent];
}

Passing content the other way, from BlazorCodeFirst code into a Razor or hand-written component, uses Component<T>(). See passing child content.

Reads are allowed, mutation is not#

Both Chrome and Body may read component state, since projecting state to UI is their purpose, but neither may mutate it. Body here means the BodyComponentBase one, not the layout's routed-content parameter. Mutating state inside either reports BCF3001, the same diagnostic that applies to a regular component's Body.

Next#

See components and reuse for calling one component from another, or elements and decorations for the element vocabulary used above.

An unhandled error has occurred. Reload 🗙