Razor Syntax and Tag Helpers
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
Razor mixes C# and HTML in .cshtml files. It is the view engine shared by
MVC and Razor Pages.
Razor syntax
@model IReadOnlyList<Product>
@{
ViewData["Title"] = "Products";
var expensive = Model.Where(p => p.Price > 100);
}
<h1>@ViewData["Title"]</h1>
<ul>
@foreach (var p in Model)
{
<li>@p.Name - @p.Price.ToString("C")</li>
}
</ul>
@if (expensive.Any())
{
<p>@expensive.Count() premium products.</p>
}
-
@expression— an implicit expression, HTML-encoded and written inline. -
@\{ … }— an explicit code block; mixed markup inside it needs<text>or@:to escape back to HTML. -
@model— declares the view’s strongly typed model (Modelin code). -
@functions— declares local methods/fields usable from markup. -
@* … *@— a Razor comment (never sent to the client, unlike an HTML comment).
_ViewImports and _ViewStart
@* Views/_ViewImports.cshtml -- applies to every view under Views/ *@
@using MyApp.Models
@namespace MyApp.Views
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@* Views/_ViewStart.cshtml -- runs before every view; usually just sets Layout *@
@{
Layout = "_Layout";
}
_ViewImports.cshtml and _ViewStart.cshtml apply to every view in their folder and subfolders; a
subdirectory can add its own to extend (not replace) the parent’s imports.
Layouts and sections
A layout is a normal view that calls @RenderBody() where the child view’s content goes, and optionally
@RenderSection for named, optional regions such as page-specific scripts:
@* Views/Shared/_Layout.cshtml *@
<!DOCTYPE html>
<html>
<head><title>@ViewData["Title"]</title></head>
<body>
@await Html.PartialAsync("_Nav")
<main>@RenderBody()</main>
@RenderSection("scripts", required: false)
</body>
</html>
@section scripts {
<script src="~/js/products.js"></script>
}
Partial views
A partial view renders a reusable fragment with no layout of its own:
<partial name="_ProductRow" model="p" />
@* or asynchronously *@
@await Html.PartialAsync("_ProductRow", p)
See Partial views.
View components
A view component is a small, reusable piece of server-rendered UI with its own logic — unlike a partial view, it does not depend on a controller action having already built its model:
public sealed class CartViewComponent(ICartService cart) : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
var items = await cart.GetItemsAsync(HttpContext);
return View(items); // renders Views/Shared/Components/Cart/Default.cshtml
}
}
@await Component.InvokeAsync("Cart")
<vc:cart></vc:cart> @* Tag Helper invocation form *@
See View components.
Built-in Tag Helpers
Tag Helpers attach server behavior to HTML-looking elements, so markup stays close to plain HTML instead of
mixing in @Html.* helper calls:
<form asp-controller="Products" asp-action="Create" method="post">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
<select asp-for="CategoryId" asp-items="Model.Categories"></select>
<button type="submit">Save</button>
</form>
<a asp-controller="Products" asp-action="Details" asp-route-id="@p.Id">@p.Name</a>
<img src="~/img/logo.png" asp-append-version="true" />
<environment include="Development">
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="~/css/site.min.css" />
</environment>
Common ones: asp-for (model-binds a field, wiring up name/id/validation attributes), asp-controller /
asp-action / asp-page / asp-route-* (URL generation), asp-validation-for / asp-validation-summary,
asp-append-version (cache-busting via a content hash), and the <environment> and <cache> elements.
Custom Tag Helpers
Subclass TagHelper and target elements or attributes:
[HtmlTargetElement("email", Attributes = "address")]
public sealed class EmailTagHelper : TagHelper
{
public string Address { get; set; } = "";
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "a";
output.Attributes.SetAttribute("href", $"mailto:{Address}");
output.Content.SetContent(Address);
}
}
<email address="support@example.com"></email>
Register custom Tag Helpers from _ViewImports.cshtml with @addTagHelper *, MyApp. See
Author Tag Helpers in ASP.NET
Core.
HTML encoding
Razor HTML-encodes @expression output by default, which is the main defense against reflected XSS. Opt out
explicitly and only for content you trust:
<p>@userComment</p> @* encoded: <script> renders as text *@
<p>@Html.Raw(trustedMarkupFromCms)</p> @* NOT encoded -- only for known-safe HTML *@
See Security Hardening for the broader XSS/CSP picture and Views in ASP.NET Core MVC.