ASP.NET
MVC
Razor
model binding
layout integration

ASP.NET MVC Razor pass model to layout

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In ASP.NET MVC, a layout page wraps many views, so it often needs shared data such as page title, navigation state, or user information. The mistake many developers make is trying to pass each view’s full model directly into the layout, which couples unrelated pages to one layout contract. The maintainable solution is giving the layout only the shared data it actually needs.

Understand the Layout Boundary

A Razor view can be strongly typed with @model, but the layout does not automatically get a second independent model. The layout is rendered as part of the same view pipeline, so if it accesses Model, it sees the child view’s model type.

That means this is fragile:

cshtml
@model MyApp.ViewModels.ProductDetailsViewModel

inside the layout, because not every child view uses ProductDetailsViewModel.

If different pages have different models, the layout should not depend on a concrete page-specific type.

Use ViewBag or ViewData for Small Shared Values

For a few simple layout values such as title or current section, ViewBag or ViewData is usually enough.

Controller:

csharp
1public ActionResult Details(int id)
2{
3    var model = new ProductDetailsViewModel
4    {
5        Id = id,
6        Name = "Mechanical Keyboard"
7    };
8
9    ViewBag.PageTitle = "Product Details";
10    ViewBag.NavSection = "Products";
11
12    return View(model);
13}

Layout:

cshtml
1<!DOCTYPE html>
2<html>
3<head>
4    <title>@(ViewBag.PageTitle ?? "MyApp")</title>
5</head>
6<body>
7    <nav>
8        Current section: @(ViewBag.NavSection ?? "Home")
9    </nav>
10    @RenderBody()
11</body>
12</html>

This is simple and works well for lightweight layout metadata.

Use a Dedicated Layout View Model for Rich Shared State

If your layout needs structured data such as notification counts, menu items, or signed-in user details, create a dedicated layout model and expose it separately instead of reusing page models.

Shared layout model:

csharp
1public class LayoutViewModel
2{
3    public string PageTitle { get; set; }
4    public string UserDisplayName { get; set; }
5    public string ActiveSection { get; set; }
6}

Then attach it to ViewBag or ViewData:

csharp
1public ActionResult Dashboard()
2{
3    var pageModel = new DashboardViewModel
4    {
5        OpenOrders = 12
6    };
7
8    ViewBag.Layout = new LayoutViewModel
9    {
10        PageTitle = "Dashboard",
11        UserDisplayName = "Ada",
12        ActiveSection = "Dashboard"
13    };
14
15    return View(pageModel);
16}

Layout:

cshtml
1@{
2    var layout = ViewBag.Layout as MyApp.ViewModels.LayoutViewModel;
3}
4
5<!DOCTYPE html>
6<html>
7<head>
8    <title>@(layout?.PageTitle ?? "MyApp")</title>
9</head>
10<body>
11    <header>
12        Signed in as @(layout?.UserDisplayName ?? "Guest")
13    </header>
14    <nav>
15        Active section: @(layout?.ActiveSection ?? "Home")
16    </nav>
17    @RenderBody()
18</body>
19</html>

This keeps page models and layout concerns cleanly separated.

Use Child Actions or Shared Components for Repeated Layout Data

If the layout needs dynamic data from services on every request, avoid duplicating controller code everywhere. In classic ASP.NET MVC, a common pattern is a child action or shared partial.

Controller:

csharp
1public class LayoutController : Controller
2{
3    public PartialViewResult Header()
4    {
5        var model = new LayoutViewModel
6        {
7            PageTitle = ViewBag.PageTitle ?? "MyApp",
8            UserDisplayName = User.Identity.IsAuthenticated ? User.Identity.Name : "Guest",
9            ActiveSection = ViewBag.NavSection ?? "Home"
10        };
11
12        return PartialView("_Header", model);
13    }
14}

Layout:

cshtml
@Html.Action("Header", "Layout")
@RenderBody()

This is often cleaner when layout data is assembled from authentication state or shared services.

Avoid Passing Full Page Models to the Layout

Trying to make the layout strongly typed to every view model usually creates one of two problems:

  • the layout becomes tied to one page type
  • developers fall back to unsafe casting and null checks everywhere

If the layout needs a value from page models across many pages, that is often a signal to extract a smaller shared abstraction instead.

A simple approach is creating a base page view model:

csharp
1public abstract class PageViewModelBase
2{
3    public string PageTitle { get; set; }
4}
5
6public class ProductDetailsViewModel : PageViewModelBase
7{
8    public int Id { get; set; }
9    public string Name { get; set; }
10}

This can work, but keep the shared base focused and small.

Common Pitfalls

One common mistake is assuming the layout can safely declare its own unrelated @model type. Another is placing too much business data in ViewBag, which becomes hard to discover and test. Developers also duplicate the same layout setup code across many controllers instead of centralizing it. Finally, casting page-specific models inside the layout makes the layout brittle and tightly coupled to individual screens.

Summary

  • A Razor layout sees the child view model, not a separate layout model automatically.
  • Use ViewBag or ViewData for small shared layout values.
  • Use a dedicated layout view model for richer shared layout state.
  • Centralize repeated layout data building with shared controller logic or partial rendering.
  • Do not couple layouts to page-specific models unless every page truly shares that type.
  • Keep layout contracts narrow so page views and shared chrome evolve independently.

Course illustration
Course illustration

All Rights Reserved.