Every page on your application shares a common DNA — the header, navigation, footer, and meta tags. Without layouts, you'd be copy-pasting this code across hundreds of views. Layouts in ASP.NET Core MVC let you define this structure once and inject unique content into it from any view, creating a clean, maintainable UI architecture.
A Layout is a Razor template (.cshtml) that defines the common HTML skeleton shared by multiple views — the <html>, <head>, <body> tags, navigation bars, footers, and shared CSS/JS references. Individual views then inject their unique content into this skeleton via @RenderBody().
┌──────────────────────────────────────┐
│ _Layout.cshtml │
│ ┌────────────────────────────────┐ │
│ │ <nav> Navigation Bar </nav> │ │
│ ├────────────────────────────────┤ │
│ │ │ │
│ │ @RenderBody() │ │
│ │ (Your View Content Here) │ │
│ │ │ │
│ ├────────────────────────────────┤ │
│ │ <footer> Footer </footer> │ │
│ ├────────────────────────────────┤ │
│ │ @RenderSection("Scripts") │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────┘
The mandatory placeholder where child view content is injected. Only ONE per layout. Think of it as the "main content area."
Named slots that child views can optionally fill. Perfect for page-specific CSS, JavaScript, or sidebar content. Use required: false for optional sections.
Executes before every view in its directory (and subdirectories). Used to set the default layout globally so you don't repeat Layout = "_Layout" in every view.
<!-- Views/Shared/_Layout.cshtml -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="@ViewData["MetaDescription"]" />
<title>@ViewData["Title"] — MyApp</title>
<!-- Global CSS -->
<link rel="stylesheet" href="~/css/app.min.css" />
<!-- Page-specific CSS (optional) -->
@RenderSection("Styles", required: false)
</head>
<body>
<!-- Shared Navigation -->
<partial name="_Navbar" />
<!-- Flash Messages -->
<partial name="_Notifications" />
<!-- MAIN CONTENT — Each view's content renders here -->
<main class="container py-4">
@RenderBody()
</main>
<!-- Shared Footer -->
<partial name="_Footer" />
<!-- Global JS (loaded last for performance) -->
<script src="~/js/app.min.js"></script>
<!-- Page-specific JS (optional) -->
@RenderSection("Scripts", required: false)
</body>
</html>
<!-- Views/Products/Index.cshtml -->
@model IEnumerable<Product>
@{
ViewData["Title"] = "All Products";
ViewData["MetaDescription"] = "Browse our complete product catalog";
}
<h1>Product Catalog</h1>
<div class="row">
@foreach (var product in Model)
{
<div class="col-md-4">
<partial name="_ProductCard" model="product" />
</div>
}
</div>
@section Styles {
<link rel="stylesheet" href="~/css/products.css" />
}
@section Scripts {
<script src="~/js/product-filter.js"></script>
<script>
ProductFilter.init({ container: '.row' });
</script>
}
<!-- Views/Shared/_AdminLayout.cshtml — Extends _Layout.cshtml -->
@{
Layout = "_Layout"; // This layout WRAPS itself inside the main layout
ViewData["Title"] = "Admin: " + ViewData["Title"];
}
<div class="d-flex">
<!-- Admin Sidebar -->
<nav class="admin-sidebar bg-dark text-white" style="width:280px; min-height:100vh;">
<div class="p-3">
<h5>⚡ Admin Panel</h5>
<a href="/admin/dashboard" class="nav-link text-white">Dashboard</a>
<a href="/admin/users" class="nav-link text-white">Users</a>
<a href="/admin/products" class="nav-link text-white">Products</a>
<a href="/admin/orders" class="nav-link text-white">Orders</a>
</div>
</nav>
<!-- Admin Content Area -->
<main class="flex-grow-1 p-4">
@RenderBody()
</main>
</div>
<!-- Views/Admin/Dashboard.cshtml — Uses the nested admin layout -->
@{
Layout = "_AdminLayout"; // Uses the admin layout (which wraps inside _Layout)
ViewData["Title"] = "Dashboard";
}
<h2>Welcome to the Admin Dashboard</h2>
<!-- This content renders inside _AdminLayout → inside _Layout -->
<!-- Views/_ViewStart.cshtml -->
@{
Layout = "_Layout"; // ALL views in /Views/ use this layout by default
}
<!-- You can also create folder-specific _ViewStart files: -->
<!-- Views/Admin/_ViewStart.cshtml -->
@{
Layout = "_AdminLayout"; // ALL views in /Views/Admin/ use the admin layout
}
// In the Layout:
@RenderSection("Scripts", required: false) // Optional: won't crash if not defined
@RenderSection("Sidebar", required: true) // Required: crash if view doesn't define it
// Conditional rendering with IsSectionDefined:
@if (IsSectionDefined("Sidebar"))
{
<aside class="sidebar">
@RenderSection("Sidebar")
</aside>
}
else
{
<aside class="sidebar">
<partial name="_DefaultSidebar" />
</aside>
}
| Feature | Layout | Partial View | View Component |
|---|---|---|---|
| Purpose | Page-level HTML skeleton | Reusable UI fragment | Self-contained UI + Logic |
| Has its own controller? | ❌ No | ❌ No | ✅ Yes (InvokeAsync) |
| Can access DI? | Via @inject | Via @inject | ✅ Full DI in class |
| Best For | Headers, footers, nav | Product cards, forms | Shopping cart, recent posts |
| Data Source | ViewData/Model | Model passed from parent | Own data from DB/service |
_ViewStart.cshtml for default layout assignment_ViewStart files for different areas (Admin, Public)required: false for optional sections like Scripts and StylesIsSectionDefined() to provide default content when sections aren't filled@RenderBody() in a layoutQ: "How do you structure layouts in a large enterprise application with multiple user roles?"
Architect Answer: "I use nested layouts with folder-specific _ViewStart.cshtml files. The root _Layout.cshtml handles the outermost HTML, meta tags, and shared CSS/JS bundles. Then I create role-specific layouts — _AdminLayout, _CustomerLayout, _VendorLayout — each defining their own sidebar, navigation, and permission-aware menus. Each of these sets Layout = '_Layout' to inherit the global structure. This creates a clean hierarchy: Global → Role-Specific → View, keeping hundreds of views DRY while allowing each section to have its own visual identity."