Razor is ASP.NET Core’s templating language for generating HTML from C# code. It is not a separate language but an extension of HTML — you write normal HTML and sprinkle in C# expressions and code blocks using the @ prefix. Razor views are compiled into C# classes at startup (or publish time), making them fast and type-safe. The key design principle: Razor HTML-encodes all output by default, preventing cross-site scripting (XSS) attacks automatically — you have to explicitly opt out with @Html.Raw() when you need unencoded output.
Core Razor Syntax
@* ── Razor file: Views/Posts/Details.cshtml ───────────────────────────── *@
@model PostDetailsViewModel @* declares the strongly-typed model *@
@* ── Expressions — @value outputs the value HTML-encoded ─────────────── *@
<h1>@Model.Title</h1> @* outputs title, HTML-encoded *@
<p>Published: @Model.PublishedAt:D</p> @* format specifier *@
<p>Views: @(Model.ViewCount + 1)</p> @* expression with operators — needs parens *@
<p>@Model.Author.Name</p> @* dot navigation *@
@* ── Code blocks — arbitrary C# ──────────────────────────────────────── *@
@{
var readTime = Math.Ceiling(Model.Body.Split(' ').Length / 200.0);
var isPopular = Model.ViewCount > 1000;
ViewBag.Title = Model.Title; @* set ViewBag inside code block *@
}
<p>~@readTime min read @(isPopular ? "⭐ Popular" : "")</p>
@* ── Conditional output ───────────────────────────────────────────────── *@
@if (Model.IsPublished)
{
<span class="badge bg-success">Published</span>
}
else
{
<span class="badge bg-warning">Draft</span>
}
@* ── Loops ───────────────────────────────────────────────────────────── *@
<ul>
@foreach (var tag in Model.Tags)
{
<li><a href="/posts/tag/@tag.Slug">@tag.Name</a></li>
}
</ul>
@* ── Inline literal text inside code block: @: ───────────────────────── *@
@for (int i = 0; i < 3; i++)
{
@: Item number @i @* outputs literal text mixed with expression *@
<br />
}
@* ── Unencoded output — use ONLY for trusted HTML ─────────────────────── *@
@Html.Raw(Model.BodyHtml) @* outputs HTML without encoding — XSS risk if untrusted! *@
@expression in Razor is HTML-encoded before output — <, >, &, and " are converted to their HTML entities. If Model.Title is <script>alert(1)</script>, Razor outputs <script>alert(1)</script> — harmless text, not executable script. This automatic encoding is the primary XSS defence in Razor views. Only use @Html.Raw() when you have generated or sanitised the HTML yourself — never for user-provided content.@if/@else blocks for simple conditional output. <span>@(Model.IsPublished ? "Live" : "Draft")</span> is more readable than a four-line if/else block. For repeated conditional logic, create a C# helper method in a @functions block or in a separate static helper class imported via @using. Keep the view template focused on presentation, not logic.@Html.Raw() with user-provided content. If a user enters <script>document.cookie</script> and you display it with @Html.Raw(userInput), the script executes in every visitor’s browser — a stored XSS attack. Always use plain @userInput for user content. Only use Html.Raw() for pre-sanitised HTML generated by trusted server-side processes (a Markdown-to-HTML converter with a sanitisation step, a CMS content field validated on input)._ViewImports.cshtml — Global Directives
@* Views/_ViewImports.cshtml — applies to ALL views in this folder and subfolders *@
@using BlogApp.Web.Models
@using BlogApp.Web.ViewModels
@using BlogApp.Application.DTOs
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @* enables all built-in Tag Helpers *@
@addTagHelper *, BlogApp.Web @* enables any custom Tag Helpers *@
@* ── Without _ViewImports, every view would need: ─────────────────────── *@
@using BlogApp.Web.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@* ... repeated in every single .cshtml file *@
Common Mistakes
Mistake 1 — Using @Html.Raw() with user content (XSS vulnerability)
❌ Wrong — executes injected scripts in visitors’ browsers:
@Html.Raw(Model.UserComment) @* user comment could contain <script> — XSS! *@
✅ Correct — use plain @Model.UserComment which HTML-encodes automatically.
Mistake 2 — Forgetting parentheses for complex expressions (parse error)
❌ Wrong — Razor parses the + as part of the expression:
<p>Total: @count + 1</p> @* renders "5 + 1", not "6" *@
✅ Correct — wrap arithmetic in parentheses: @(count + 1).