Layouts and Sections — Master Page Templates

A layout in Razor MVC serves the same purpose as a master page in older ASP.NET — it defines the common page shell (navigation, footer, scripts, CSS links) shared across all pages, with a placeholder where each page’s content is injected. Without layouts, every view would repeat the full HTML document structure. A section extends the layout model further: child views can contribute content (page-specific scripts, styles, a sidebar) to named slots in the layout, enabling flexible per-page customisation within a consistent shell.

Creating a Layout

@* Views/Shared/_Layout.cshtml — the master template *@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewBag.Title — BlogApp</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    @* optional section — child views can add page-specific styles *@
    @await RenderSectionAsync("Styles", required: false)
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <a class="navbar-brand" href="/">BlogApp</a>
        <!-- navigation items -->
    </nav>

    <main role="main" class="container mt-4">
        @* THIS is where each view's content is injected *@
        @RenderBody()
    </main>

    <footer class="footer mt-auto py-3">
        <span>© @DateTime.Now.Year BlogApp</span>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @* optional section — child views add page-specific scripts here *@
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Note: @RenderBody() is the single required call in a layout — it marks where the child view’s content is injected. A layout can only have one @RenderBody() call. @RenderSection("Scripts", required: false) is optional — if a child view does not define a @section Scripts { } block, nothing is rendered for that slot (because required: false). Making a section required: true causes a runtime exception if any view that uses this layout does not provide the section. Use required: true for sections that every page genuinely must provide (like a PageTitle section).
Tip: Put page-specific scripts in a @section Scripts { } block that renders at the bottom of the layout (after jQuery and Bootstrap). This ensures jQuery is loaded before your page scripts run. A common mistake is putting scripts in the <head> of the layout and finding jQuery is not yet available when inline scripts reference $. The Scripts section at the end of <body> is the correct location for page-specific JavaScript.
Warning: If a view defines a section that is not declared in the layout with @RenderSection(), ASP.NET Core throws an InvalidOperationException: The following sections have been defined but have not been rendered by the page at '.../_Layout.cshtml': 'Scripts'. This means you cannot add a section to a view without also adding the corresponding @RenderSection() call to every layout that view uses. When adding new sections to views, always check that all layouts in use declare that section.

_ViewStart.cshtml and Per-View Layout Override

@* Views/_ViewStart.cshtml — sets default layout for ALL views ────────────── *@
@{
    Layout = "_Layout";   @* all views use _Layout by default *@
}

@* ── Override layout for a specific view ─────────────────────────────── *@
@* Views/Admin/Dashboard.cshtml *@
@{
    Layout = "_AdminLayout";   @* this view uses a different layout *@
    ViewBag.Title = "Dashboard";
}
<h1>Admin Dashboard</h1>
@* rendered inside _AdminLayout instead of _Layout *@

@* ── No layout (standalone HTML, for printing or email) ────────────────── *@
@* Views/Reports/Invoice.cshtml *@
@{
    Layout = null;   @* no layout wrapper — bare HTML *@
}
<!DOCTYPE html>
<html><body>...</body></html>

@* ── Child view using sections ──────────────────────────────────────────── *@
@* Views/Posts/Details.cshtml *@
@model PostDetailsViewModel
@{
    ViewBag.Title = Model.Title;
}

<article>
    <h1>@Model.Title</h1>
    <div>@Html.Raw(Model.BodyHtml)</div>
</article>

@section Scripts {
    @* page-specific JS — rendered at bottom of layout *@
    <script src="~/js/highlight.min.js"></script>
    <script>hljs.highlightAll();</script>
}

Common Mistakes

Mistake 1 — Defining a section the layout does not declare (exception)

❌ Wrong — layout has no @RenderSection(“Sidebar”) but view defines @section Sidebar { }.

✅ Correct — add @await RenderSectionAsync(“Sidebar”, required: false) to the layout first.

Mistake 2 — Putting page-specific scripts in the layout head (jQuery not loaded)

❌ Wrong — script in <head> runs before jQuery loads; $ is undefined.

✅ Correct — put page scripts in @section Scripts { } which renders after jQuery at end of body.

🧠 Test Yourself

A layout calls @await RenderSectionAsync("Sidebar", required: true). A view that uses this layout does not define @section Sidebar { }. What happens?