Razor Syntax

This section documents ASP.NET MVC 5.3.x, ASP.NET Web API 2.2, ASP.NET Web Pages 3, OWIN/Katana, SignalR 2, and ASP.NET Identity 2 — all running on .NET Framework 4.8.1 — the System.Web-hosted MVC framework, its routing, Razor views, HTML helpers, model binding, filters, and the OWIN-based authentication/Identity stack — as described by the official documentation at Microsoft Learn (plus Web API, Web Pages, SignalR, and Identity), which are the reference these pages are written and verified against.

This is not ASP.NET Core MVC. ASP.NET MVC 5 is the pre-Core, System.Web-hosted MVC framework; it is functionally frozen and receives only security fixes. For the current, cross-platform MVC framework see MVC Controllers and Views under ASP.NET Core (Blazor).

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page documents the Razor view engine as it ships with ASP.NET MVC 5.3.x / Web Pages 3 on .NET Framework 4.8.1 — not ASP.NET Core’s Razor, which adds Tag Helpers and Blazor component syntax on top of the same expression language (see Razor Syntax and Tag Helpers).

Where Razor came from

Razor shipped in January 2011 as part of MVC 3, alongside ASP.NET Web Pages 1 — a lightweight, non-MVC way to build .cshtml sites. It replaced the original MVC 1/2 WebForms view engine (.aspx/.ascx views with <%= %>/<%: %> code-nuggets and a ViewPage base class inherited from Web Forms), which was verbose, easy to mis-encode, and dragged in Web Forms concepts (the page directive, control parsing) that MVC’s action/view model did not need. Razor was designed from scratch for view templates: a minimal-punctuation syntax the parser can distinguish from literal markup by context, HTML-encoding by default, and no dependency on the Web Forms control tree.

Implicit and explicit expressions

<p>Hello, @Model.UserName!</p>              <!-- implicit expression: @ + a single identifier/member chain -->
<p>Total: @(Model.Price * Model.Quantity)</p>  <!-- explicit expression: @( ... ) for anything the parser can't disambiguate -->

Code blocks

@{
    var greeting = "Welcome back";
    ViewBag.Title = "Home";
}
<h1>@greeting, @Model.UserName</h1>

@: and <text>

Inside a code block, a line of literal text needs an explicit marker so Razor doesn’t try to parse it as C#:

@foreach (var item in Model.Items)
{
    @:Item: @item.Name          <!-- single line of literal markup -->
    <text>Qty: @item.Quantity</text>   <!-- multi-token literal run without wrapping HTML -->
}

Comments

@* This comment never reaches the rendered output, unlike an HTML <!-- --> comment. *@

Automatic HTML encoding and Html.Raw

Every @-expression is HTML-encoded by default, which is what makes Razor safe against reflected XSS by default (see Security Hardening):

<p>@Model.Comment</p>              <!-- "<script>" is encoded to "&lt;script&gt;" -->
<div>@Html.Raw(Model.TrustedHtml)</div>   <!-- opt out ONLY for content you generated/sanitized yourself -->

Html.Raw must never be used on user-supplied input without sanitizing it first — it is the single most common source of stored/reflected XSS in MVC 5 codebases.

@model, @using, @inherits, @functions, @helper

@model MyApp.Models.ProductViewModel
@using MyApp.Helpers
@inherits System.Web.Mvc.WebViewPage<MyApp.Models.ProductViewModel>   <!-- rarely needed; @model implies this -->

<h1>@Model.Name</h1>

@helper Star(int filled)                <!-- an inline, reusable markup-producing helper, scoped to this view -->
{
    for (int i = 0; i < 5; i++) { <span class="@(i < filled ? "on" : "off")">*</span> }
}
@Star(Model.Rating)

@functions {
    // C# members (not markup-producing) usable elsewhere in the same view
    string FormatPrice(decimal p) => p.ToString("C");
}
<p>@FormatPrice(Model.Price)</p>

@model is sugar for @inherits System.Web.Mvc.WebViewPage<T>, giving the view a strongly typed Model.

_ViewStart.cshtml

Views/_ViewStart.cshtml (and an area-local Views/{Area}/_ViewStart.cshtml) runs before every view in its folder and below, and conventionally sets the shared Layout:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Views/web.config namespaces

Namespaces listed in Views/web.config under <system.web.webPages.razor><pages><namespaces> are available to every view without an explicit @using (see Getting Started for the full snippet) — System.Web.Mvc.Html (the HTML helpers, see HTML Helpers and Forms) is imported this way by default.

ASP.NET Web Pages 3: the third framework of this era

ASP.NET Web Pages is .cshtml Razor without MVC — a page-per-file model closer to classic ASP or PHP than to MVC’s controller/action separation, aimed at simple sites. It shares the Razor parser and Microsoft.AspNet.WebPages runtime with MVC (see Getting Started) but has its own conventions:

@{
    // default.cshtml -- the file itself is the "action"
    Page.Title = "Home";
}
<h1>@Page.Title</h1>
@RenderPage("~/_nav.cshtml")           <!-- Web Pages' equivalent of a partial -->
  • An App_Start/_AppStart.cshtml runs once at startup, analogous to MVC’s Application_Start.

  • WebSecurity (Microsoft.Web.WebPages.OAuth / WebMatrix.WebData) provided Web Pages' own membership and OAuth login support, parallel to (and predating) ASP.NET Identity — see Authentication, Identity, and OWIN.

  • Web Pages is rarely chosen for new work even within the MVC 5 era; it is documented here because it explains why Microsoft.AspNet.WebPages appears as a dependency of every MVC 5 project even when no .cshtml file is ever rendered outside the Views folder.

Next: Views, Layouts, and Partials builds on this syntax to cover layouts, sections, and reusable view fragments.