ASP.NET MVC · Controllers & Views

Partial Views

When the same piece of HTML appears on several pages — a product card, a cart summary, a menu — write it once as a partial view and reuse it everywhere.

Intermediate 6 min Lesson 11 of 37Covers ASP.NET MVC 5.2 on .NET Framework 4.8.
Before you start:Layout Pages
0/37 in ASP.NET MVC
On this page · 6 min · Intermediate

Learning objectives#

  • Create and render partial views
  • Pass a model to a partial
  • Choose between Html.Partial and Html.RenderPartial
  • Use child actions for partials that need their own data

Explanation#

A partial view is a .cshtml file without a layout, usually named with a leading underscore (_ProductCard.cshtml) and stored in the controller’s folder or Views/Shared.

@Html.Partial("_ProductCard", product) renders it and returns the HTML as a string. @{ Html.RenderPartial("_ProductCard", product); } writes directly to the response — slightly faster, same result.

A partial receives the model you pass. If you pass none, it gets the parent view’s model, which can cause “The model item passed into the dictionary is of type…” errors.

A child action renders a partial with its own data: @Html.Action("CartSummary", "Cart") runs a full action (with its own model loading) and inserts its output. Mark it [ChildActionOnly] so it cannot be requested by URL.

Controllers can also return PartialView("_Rows", items) — perfect for AJAX requests that refresh part of a page.

Syntax#

Razor / Markup Illustrative
@Html.Partial("_ProductCard", product)
@{ Html.RenderPartial("_ProductCard", product); }
@Html.Action("CartSummary", "Cart")          @* child action *@

Code example: A product card partial and a cart-summary child action#

C#_ProductCard.cshtml Illustrative
// Models/Product.cs
namespace ShopSite.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}


// Controllers/ShopController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using ShopSite.Models;


namespace ShopSite.Controllers
{
    public class ShopController : Controller
    {
        public ActionResult Index()
        {
            var products = new List<Product>
            {
                new Product { Id = 1, Name = "Gel Pen", Price = 20m },
                new Product { Id = 2, Name = "Notebook", Price = 60m },
            };
            return View(products);
        }


        // Only callable from a view with Html.Action
        [ChildActionOnly]
        public ActionResult CartSummary()
        {
            ViewBag.Count = 3;          // in a real app: read from the session or database
            return PartialView("_CartSummary");
        }
    }
}


@* Views/Shop/Index.cshtml *@
@model List<Product>
@Html.Action("CartSummary")
<h1>Products</h1>
@foreach (var p in Model)
{
    @Html.Partial("_ProductCard", p)
}


@* Views/Shared/_ProductCard.cshtml *@
@model Product
<div class="card">
    <h2>@Model.Name</h2>
    <p>@Model.Price.ToString("0.00") rupees</p>
</div>


@* Views/Shop/_CartSummary.cshtml *@
<p class="cart">Cart: @ViewBag.Count items</p>
Expected output
GET /Shop
200 OK
<p class="cart">Cart: 3 items</p>
<h1>Products</h1>
<div class="card">
    <h2>Gel Pen</h2>
    <p>20.00 rupees</p>
</div>
<div class="card">
    <h2>Notebook</h2>
    <p>60.00 rupees</p>
</div>

GET /Shop/CartSummary
500 Internal Server Error

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. Index passes a list of products to its view.
  2. Html.Action("CartSummary") runs the child action, which sets its own data and returns the _CartSummary partial.
  3. For each product, Html.Partial renders _ProductCard from Views/Shared with that product as its model.
  4. Partials have no layout, so only their markup is inserted.
  5. Requesting /Shop/CartSummary directly fails because of [ChildActionOnly] — the error page shows instead of the partial.

Real-world use case#

A bookshop shows the same book card on the home page, search results, category pages and the wishlist. One _BookCard partial means a design change (adding a “Bestseller” badge) is made once.

Common mistakes#

Passing the wrong model to a partial

Calling @Html.Partial("_ProductCard") without a model passes the parent’s List<Product>, and the partial that expects Product throws a model type error.

Database calls inside a partial

A partial that loads its own data from a static helper is hard to test. Use a child action or load the data in the parent action.

Forgetting [ChildActionOnly]

Without it, /Shop/CartSummary is a public URL returning a bare HTML fragment.

Best practices#

  • Prefix partial names with an underscore
  • Always pass the model explicitly
  • Use child actions for widgets that need their own data
  • Return PartialView from actions used by AJAX

Remember this#

Partial = reusable piece, no layout.

Html.Partial(name, model) — pass the model.

Needs its own data? Html.Action + [ChildActionOnly].

Practice exercise#

Your task: Create a _Price partial that takes a decimal and shows it with two decimals and a “Sale!” label when it is under 50. Use it twice in a view.

Need a hint?
@model decimal in the partial; call @Html.Partial("_Price", 30m).
Show solution
C# Illustrative
// Controllers/SaleController.cs
using System.Web.Mvc;


namespace ShopSite.Controllers
{
    public class SaleController : Controller
    {
        public ActionResult Index() => View();
    }
}


@* Views/Sale/Index.cshtml *@
@Html.Partial("_Price", 30m)
@Html.Partial("_Price", 120m)


@* Views/Shared/_Price.cshtml *@
@model decimal
<p>@Model.ToString("0.00")@if (Model < 50) {<strong> Sale!</strong>}</p>
Expected output
GET /Sale
200 OK
<p>30.00<strong> Sale!</strong></p>
<p>120.00</p>

Mini quiz#

Partial Views quizMultiple choice Question 1 of 3

What is the difference between Html.Partial and Html.RenderPartial?

Score so far: 0/0

Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.