View Components in ASP.NET Core MVC

4.25 (8 votes)

The ASP.NET Core MVC framework (previously known as MVC 6) includes a new feature called View Components. Here, I take a look at them, the kind of problems they are designed to solve and how to use them.

Most templated web sites have content that's repeated across multiple pages. This site includes a tag cloud, a list of recent comments and an archive listing. These items or widgets appear on every page of the site. The simplest way to manage this in traditonal ASP.NET MVC without repeating code everywhere is to use Child Actions - controller action methods decorated with the [ChildActionOnly] attribute called from within a view or layout file via the Html.Action helper. Or, if the content of your widget doesn't require any interaction with the server (e.g. because you already have the data it needs), you can use a partial view invoked within the view or layout by the Html.RenderPartial helper method. View Components are an alternative to both of these approaches.

A View component is a class that inherits from ViewComponent and implements an Invoke or InvokeAsync method. The return type of the method will depend on what you want to render to the browser, but most likely, you will make use of a Razor View file to render templated content, so your Invoke or InvokeAsync method will return an IViewComponentResult. You could also return a string or an HtmlString, for example. Here is an example of a very simple View Component:

public class ContactViewComponent : ViewComponent
    public IViewComponentResult Invoke()
        return View();

The component class can be placed anywhere in the structure of an MVC application, but the recommendation is that you create a folder called ViewComponents to house them in. There is no logic in this component. All it does is return a view, but which view? The MVC framework will search two locations:


The first location is for controller-specific views, and is not likely to be used very often. It is potentially useful if you want to provide a different view for the same component depending on the controller that owns the current action being invoked. The second location is much more likely to be used. You create a folder called Components in the Views/Shared folder, and then create a subfolder named after the component. The convention is that the name of the component is the class name without the "ViewComponent" suffix - or in this example, "Contact".

Here is this content of the Default.cshtml file:

@model ViewComponentTest.ViewModels.Contact.ContactViewModel

<form method="post" asp-action="contact" asp-controller="home">
        Email: <input asp-for="Email" /><br />
        Name: <input asp-for="Name" /><br />
        Message: <textarea asp-for="Message"></textarea><br />
        <input type="submit" value="Submit" class="btn btn-default" />

It's a standard strongly typed partial view, although it doesn't have to be strongly typed. In this intance, its model is a ContactViewModel designed to help create with the creation of a contact form based on TagHelpers.

The content is rendered as a result of a call to Component.InvokeAsync() from within a view or layout, with the name of the component being passed as a parameter:

@await Component.InvokeAsync("Contact")

At the time of writing, RC2 is not yet ready, so there are other methods available for rendering components, including Component.Invoke, Component.Render and so on. These are being removed in RC2. If you don't want to suffer breaking changes, stick solely with the asynchronous version of Invoke.

The next example is a bit more complex. It demonstrates how to create a view component that's responsible for displaying data obtained from an external source such as a database:

public class BestSeller : ViewComponent
    private IBookService _service;
    public BestSeller(IBookService service)
        _service = service;
    public async Task<IViewComponentResult> InvokeAsync(int numberToTake)
        var mostPopular = await _service.MostPopular(numberToTake);
        return View(mostPopular);

The view component is named BestSeller (without the "ViewComponent" suffix"). It has a dependency on an instance of an IBookService, which is injected into the constructor and is resolved by the ASP.NET Core Dependency Injection framework. This example features the asynchronous InvokeAsync method, and accepts a parameter representing the number of items to select from the database. This represents one of the major improvements over Child Actions, which do not support asynchronous. Here is an implementation of the BookService that makes use of the in-memory version of Entity Framework (which is designed for testing and demos):

public class BookService : IBookService
    private BookContext _context;
    public BookService(BookContext context)
        _context = context;
        var books = new List<Book>
            new Book {BookId = 1, Author = "Kate Atkinson", Title = "A God In Ruins", Price = 7.99m },
            new Book {BookId = 2, Author = "Renee Knight", Title = "Disclaimer", Price = 7.99m },
            new Book {BookId = 3, Author = "James Patterson", Title = "Private Sydney", Price = 7.99m },
            new Book {BookId = 4, Author = "Michael Punke", Title = "The Revenant", Price = 7.99m },
            new Book {BookId = 5, Author = "Celia Imrie", Title = "Not Quite Nice", Price = 7.99m },
            new Book {BookId = 6, Author = "Harlan Coben", Title = "The Stranger", Price = 7.99m },
            new Book {BookId = 7, Author = "Emma Donoghue", Title = "Room", Price = 8.99m },
            new Book {BookId = 8, Author = "Laura Barnett", Title = "The Versions of Us", Price = 7.99m }
    public async Task<List<Book>> MostPopular(int numberToTake)
        return await _context.Books.OrderBy(b => b.BookId).Take(numberToTake).ToListAsync();

And finally, here is the view, located in /Views/Shared/Components/BestSeller

@model List<Book>
<h3>Best sellers</h3>
<ul style="padding-left:0;list-style-type:none;">
    @foreach (var book in Model)
        <li>@book.Title - @book.Author (@book.Price.ToString("c"))</li>

When this component is called from within the view, the argument representing the number to take is passed along with the name of the component:

@await Component.InvokeAsync("BestSeller", 5)

From RC2, this behavour will change so that the parameter representing the number to take will be passed as either a dictionary or an anonymous type:

@await Component.InvokeAsync("BestSeller", new { numberToTake = 5 })

And the result is injected into the view output:

View Components


This article looks at View Components, a new means for managing partial page content within ASP.NET Core MVC web sites. It shows what they are and how to use them. It also discusses a couple of upcoming changes when RC 2 is available.

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 20850


- apr

What about caching?

The old child action results can be cached, and that yields extreme performance boost.

- Mike


You can use the Cache TagHelper for that. Dave Paquette has a nice writeup about them.

Recent Comments

Satyabrata Mohapatra 23/07/2017 16:43
In response to Razor Pages - The Elevator Pitch
@Dale Severin You can continue to build apps using web pages....

Satyabrata Mohapatra 23/07/2017 16:40
In response to Sending Email in Razor Pages
Thanks for sharing...learned a lot...

Gfw 22/07/2017 11:53
In response to Sending Email in Razor Pages
Question... Does System.Net.Mail support SSL?...

Dale Severin 20/07/2017 03:38
In response to Razor Pages - The Elevator Pitch
I work with razor web pages extensively. I appreciate the rapid development it permits me to I am as...

Obinna Okafor 14/07/2017 01:19
In response to Routing in Razor Pages
Thank you, Mike. Good post....

Satyabrata Mohapatra 11/07/2017 16:02
In response to Routing in Razor Pages
Very powerful routing system!!...

Cyrus 05/07/2017 03:41
In response to Razor Pages - Getting Started With The Preview
How can I trim packages and services as much as possible to use just razor pages? I don’t want to to...

Harris Boyce 04/07/2017 04:17
In response to Razor Pages - The Elevator Pitch
As a developer of a couple "trivial" web pages applications used by non-profits that wouldn't have I...

Cyrus 28/06/2017 20:25
In response to Razor Pages - Getting Started With The Preview
.net core 2.0 preview 2: <a...

ojorma 17/06/2017 09:24
In response to Razor Pages - The Elevator Pitch
Finally I can say goodbye to webforms...