Razor Syntax (cshtml)
Razor is the template language ASP.NET Core uses to build HTML on the server. A Razor file has the extension .cshtml and is mostly ordinary HTML, with C# mixed in wherever the page needs data: a patient's name, a loop over today's appointments, a message that appears only after 8 pm. Razor Pages, MVC views and Blazor components all use it.
In this lesson you will learn how Razor switches between HTML and C#, how it protects you from script injection by encoding output, the directives at the top of a file, and how layouts, sections, partials and tag helpers keep pages consistent.
The @ symbol
The @ character switches from HTML to C#. Razor then works out by itself where the C# ends and HTML starts again.
@page
@{
var patients = new[] { "Aarav", "Priya", "Kabir" };
int fee = 500;
bool eveningOpen = DateTime.Now.Hour < 20;
}
<h2>Today's queue (@patients.Length patients)</h2>
<ol>
@foreach (var name in patients)
{
<li>@name</li>
}
</ol>
<p>Consultation fee: ₹@fee, follow-up: ₹@(fee / 2)</p>
@if (eveningOpen)
{
<p>Evening OPD is open until 8 pm.</p>
}
else
{
<p>Closed for today. Please book online for tomorrow.</p>
}Before 8 pm the browser receives this HTML (blank lines removed):
<h2>Today's queue (3 patients)</h2>
<ol>
<li>Aarav</li>
<li>Priya</li>
<li>Kabir</li>
</ol>
<p>Consultation fee: ₹500, follow-up: ₹250</p>
<p>Evening OPD is open until 8 pm.</p>The forms of @ you just used:
| Syntax | Name | Example |
|---|---|---|
@name | Implicit expression | @patients.Length, @Model.Name |
@( ... ) | Explicit expression | @(fee / 2), @(a + b), generic calls like @(Get<int>()) |
@{ ... } | Code block | Declare variables, run statements; nothing is output by itself |
@if, @foreach, @for, @switch, @while | Control structures | HTML inside the braces is output when the code runs |
@* ... *@ | Razor comment | Removed completely; never sent to the browser |
An implicit expression stops at a space, so @fee / 2 would print 500 / 2. Use the explicit form @(fee / 2) for calculations. Razor also recognises email addresses: help@shanticlinic.in is printed as text, not treated as code. To print a literal @ before a word, write @@.
Text inside code blocks
Inside a code block, Razor treats a line that starts with an HTML tag as markup. Plain text without a tag needs a marker:
@foreach (var slot in new[] { "10:00", "10:15", "10:30" })
{
@:Slot @slot is free.
<text>Book now.</text>
}@: outputs the rest of the line as text, and <text>...</text> outputs its content without adding a real HTML element.
Automatic HTML encoding
Every @ expression is HTML-encoded before it is written. Characters that mean something in HTML, such as <, >, & and quotes, become safe entities. This is your main protection against cross-site scripting (XSS), where a user submits a script that would otherwise run in other users' browsers.
@{
string review = "<script>alert('hacked')</script> Great doctor!";
string city = "पुणे";
}
<p>@review</p>
<p>@city</p><p><script>alert('hacked')</script> Great doctor!</p>
<p>पुणे</p>The script arrives as harmless text. The Hindi city name is also encoded, as numeric character references; browsers display it correctly as "पुणे", but the page source is hard to read and slightly larger. By default the encoder leaves only basic Latin characters unencoded. To keep Devanagari as it is, register a wider encoder in Program.cs:
using System.Text.Encodings.Web;
using System.Text.Unicode;
builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Devanagari));Text you type directly in the .cshtml markup, such as the ₹ sign in the first example, is not encoded; only @ expressions are.
Directives
Lines at the top of a file that start with @ and a keyword are directives. They configure the whole file:
| Directive | Purpose |
|---|---|
@page | Makes the file a Razor Page with its own URL (Razor Pages only, must be the first line) |
@model Type | Declares the type of Model for the view or page |
@using Namespace | Adds a using for this file |
@inject IService Name | Injects a service from DI into a property called Name |
@section Name { ... } | Defines content that the layout places in a named spot |
@functions { ... } | Adds C# methods and fields to the generated class |
@addTagHelper *, Assembly | Enables the tag helpers from an assembly |
Layouts, sections and _ViewStart
Every page on a site shares a header, navigation and footer. A layout holds that shared HTML, and each page supplies only its own part. Pages/Shared/_Layout.cshtml (or Views/Shared/_Layout.cshtml in MVC):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"] - Shanti Clinic</title>
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
<header><a asp-page="/Index">Shanti Clinic, Pune</a></header>
<main>@RenderBody()</main>
<footer>Open Mon-Sat, 10:00-13:00</footer>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>@RenderBody() is where the page's own content goes. RenderSectionAsync("Scripts", required: false) is an optional slot that a page can fill:
@page
@{
ViewData["Title"] = "Book an appointment";
}
<h1>Book an appointment</h1>
<p>Choose a doctor and a free slot.</p>
@section Scripts {
<script src="~/js/booking.js"></script>
}Two special files apply to every page in their folder and below:
_ViewStart.cshtmlruns before each page; the template's version contains@{ Layout = "_Layout"; }, so you never repeat it._ViewImports.cshtmlholds shared directives, such as@using ShantiClinic.Modelsand@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers.
Partials and tag helpers
A partial view is a reusable piece of markup, such as a doctor card used on several pages. Create Pages/Shared/_DoctorCard.cshtml with @model Doctor, then render it with <partial name="_DoctorCard" model="doctor" />.
Tag helpers are attributes and elements, starting with asp-, that run C# on the server to produce correct HTML:
<a asp-page="/Doctors/Details" asp-route-id="3">builds the link/Doctors/Details/3(or?id=3) from routing.<input asp-for="Input.PatientName" />sets thename,id,value,typeand validation attributes from the model property.<span asp-validation-for="Input.PatientName"></span>shows that field's validation message.<link asp-append-version="true">adds a version to static file URLs.<environment include="Development">...</environment>outputs its content only in that environment.
Tag helpers look like normal HTML, so designers can still read the files, and they keep URLs and form field names in sync with your C# code.
Common mistakes
@fee * 2instead of@(fee * 2). The implicit form stops at the space and prints the rest as text.- Using
Html.Rawon user data. It disables encoding and opens an XSS hole. - Forgetting
@addTagHelperin_ViewImports.cshtml.asp-attributes are then sent to the browser unchanged and links are broken. - Heavy logic in views. Calculations and database calls belong in the page model or controller; views should only display data.
- Putting
@pageanywhere but the first line. The file then stops being a routable page.
Key points
@switches to C#: implicit expressions (@name), explicit expressions (@(a + b)), code blocks (@{ }) and control structures (@if,@foreach).- All
@output is HTML-encoded automatically;@Html.Rawturns this off and must never receive user input. - Directives such as
@page,@model,@injectand@sectionconfigure a file. - Layouts use
@RenderBody()and sections;_ViewStart.cshtmlsets the layout and_ViewImports.cshtmlshares directives. - Partials reuse markup, and
asp-tag helpers generate links, form fields and validation messages from your C# model.
Exercise
Write a Razor Page Pages/Fees.cshtml for a coaching centre in Lucknow. In a code block, create an array of (string Course, int Fee) tuples: ("Maths", 1200), ("Physics", 1100), ("Chemistry", 1000). Show them in an HTML table with a header row, show the total fee below the table, and show the message "Sibling discount: 10%" only when the total is more than ₹3,000. Set the page title to "Course Fees".
Show solution
@page
@{
ViewData["Title"] = "Course Fees";
var courses = new (string Course, int Fee)[]
{
("Maths", 1200), ("Physics", 1100), ("Chemistry", 1000)
};
int total = courses.Sum(c => c.Fee);
}
<h1>Course Fees</h1>
<table>
<tr><th>Course</th><th>Fee</th></tr>
@foreach (var c in courses)
{
<tr><td>@c.Course</td><td>₹@c.Fee</td></tr>
}
</table>
<p>Total: ₹@total</p>
@if (total > 3000)
{
<p>Sibling discount: 10%</p>
}The total is 3,300, so the discount message appears. courses.Sum works because System.Linq is among the implicit usings, and ViewData["Title"] is read by the layout's <title> element.
Quick Quiz
1. What does @(price * 2) do that @price * 2 does not?
The implicit form stops at the space; the explicit form evaluates everything inside the brackets.
2. A user's review contains <script>. What does <p>@review</p> output?
Razor encodes all @ output, which prevents cross-site scripting.
3. Where does a page's own content appear inside a layout?
RenderBody marks the spot for the page content; sections fill other named spots.
4. Which file usually contains @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers?
_ViewImports.cshtml shares directives with every view or page in its folder and below.
Learn ASP.NET Core with a mentor
Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
