All articles

Areas in ASP.NET Core MVC: Setup, Routing and asp-area Links

How to use areas in ASP.NET Core MVC: folder structure, the [Area] attribute, the {area:exists} route, view resolution, asp-area links, and securing an area.

0 · log in to like, save & follow Share on LinkedIn Share on X

As an MVC application grows, a flat Controllers folder with thirty files stops scaling — the admin screens, the customer store, and the API endpoints all blur together. Areas are ASP.NET Core MVC's built-in answer: self-contained sections of the app, each with its own controllers, views, and URL prefix, without spinning up separate projects.

Areas in ASP.NET Core MVC: Setup, Routing and asp-area Links

How to Use Areas in ASP.NET Core MVC

This guide sets up an Admin area on .NET 10 — folder structure, routing, link generation, and the sharp edges — verified against the companion repository.

What an area gives you

  • A URL namespace: everything under /Admin/... routes to the area's controllers.
  • A folder namespace: Areas/Admin/Controllers and Areas/Admin/Views keep the section's code together.
  • Independent view resolution: the area's views can differ completely from the main app's, while still sharing layouts when you want.

Typical splits: Admin, Account, Api, or per-business-domain sections (Billing, Catalog) in a modular monolith.

Folder structure

AspNetCoreMvcExamples/
├── Areas/
│   └── Admin/
│       ├── Controllers/
│       │   └── DashboardController.cs
│       └── Views/
│           └── Dashboard/
│               └── Index.cshtml
├── Controllers/
├── Views/
└── Program.cs

Only the Areas/{name}/Controllers and Areas/{name}/Views conventions matter; Models can live wherever your architecture puts them.

The controller: [Area] is mandatory

[Area("Admin")]
public class DashboardController : Controller
{
    public IActionResult Index()
    {
        ViewBag.Stats = new Dictionary<string, int>
        {
            ["Products"] = Catalog.Products.Count,
            ["Out of stock"] = Catalog.Products.Count(p => p.Stock == 0),
        };
        return View();
    }
}

The [Area("Admin")] attribute is what binds the controller to the area — the folder location alone does nothing. Forgetting the attribute is the classic areas bug: the controller still exists, but it matches the default route, and its views resolve from the wrong folder.

Routing

Register an area route before the default route, using the {area:exists} constraint:

app.MapControllerRoute(
    name: "areas",
    pattern: "{area:exists}/{controller=Dashboard}/{action=Index}/{id?}");

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

{area:exists} only matches URL prefixes that correspond to a registered area, so /Products still falls through to the default route while /Admin/Dashboard hits the area. Verified:

GET /Admin/Dashboard   → 200 (Areas/Admin/Views/Dashboard/Index.cshtml)
GET /Products          → 200 (Views/Products/Index.cshtml)

Attribute routing works too, and larger apps often prefer it: [Route("admin/[controller]/[action]")] on a base AdminController gives every admin controller the prefix without pattern juggling.

View resolution and layouts

For an area request, Razor searches Areas/Admin/Views/{controller}/, then Areas/Admin/Views/Shared/, then the app-level Views/Shared/. That fallback is exactly what makes layout sharing easy — the area view can opt into the main layout explicitly:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
    ViewData["Title"] = "Admin dashboard";
}

If the area needs its own chrome (a sidebar-heavy admin shell), drop a _ViewStart.cshtml and _Layout.cshtml into Areas/Admin/Views/ and the whole area switches without touching the public site. Remember to add a _ViewImports.cshtml in the area (or rely on the root one) so tag helpers work.

Generating links across areas

Tag helpers need to be told about area boundaries — this is the second classic bug. From an area view, a link back to the main app must set asp-area="":

<a asp-area="" asp-controller="Products" asp-action="Index">Back to the store</a>

And from the main app into the area:

<a asp-area="Admin" asp-controller="Dashboard" asp-action="Index">Admin</a>

Omit asp-area and the current request's area is assumed — links inside the admin area silently point at /Admin/Products instead of /Products, which 404s. The same applies to RedirectToAction: pass new { area = "" } when crossing out of an area.

Securing an area

Areas are an organizational feature, not a security boundary — /Admin/... URLs are as public as any other until you say otherwise. The clean approach is an authorization policy applied to the whole area:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));

app.MapControllerRoute(
        name: "areas",
        pattern: "{area:exists}/{controller=Dashboard}/{action=Index}/{id?}")
    .RequireAuthorization("AdminOnly");

or [Authorize(Policy = "AdminOnly")] on a shared base controller for the area. Either way, the rule lives in one place instead of on thirty actions.

Areas vs. alternatives

  • Feature folders (organizing by feature instead of by controller/view kind) solve code organization without URL prefixes — some teams combine both.
  • Separate projects / Razor class libraries are the step up when a section needs independent deployment or reuse across apps.
  • Minimal API groups (MapGroup("/admin")) are the equivalent concept for endpoint-style APIs.

Areas hit the sweet spot when one deployable app has a few clearly distinct sections with their own screens.

Summary

Create Areas/{Name}/Controllers and Views, mark controllers with [Area], register the {area:exists} route before the default, and be explicit with asp-area on every cross-boundary link. Add an authorization policy at the area edge, and you have a tidy admin section inside one deployable app. The complete working area is in the companion repository.

Frequently asked questions

What are areas in ASP.NET Core MVC?

Areas split a large MVC app into self-contained sections, such as Admin or Billing, each with its own controllers and views.

Why isn't my area controller being found?

The controller needs the [Area("Admin")] attribute; the folder alone does nothing. Also add the area route ({area:exists}/{controller}/{action}) before the default route.

How do I link to a page in an area?

Use the asp-area tag helper, for example asp-area="Admin". To link from an area back to the main app, set asp-area="".

How do I secure an entire area?

Apply an authorization policy to all controllers in the area, for example with a filter or a base controller carrying [Authorize(Policy = "Admin")].

Related reading

Enjoyed this article? Get the best GeeksArray articles in your inbox — once a week, no spam, unsubscribe anytime.

Comments (0)

Log in to join the conversation.

No comments yet — be the first to share your thoughts.