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>
@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).@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.@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.