Razor views that stay maintainable
Build typed pages with layouts, tag helpers, encoded output, and reusable presentation.
Here's what you'll be able to do.
- Use @model, expressions, loops, and shared layouts
- Generate links and forms with tag helpers
- Explain why default HTML encoding matters
Know how a controller returns a view. Open Views/Tasks/Index.cshtml and Views/Shared/_Layout.cshtml.
Understand the idea
Razor is a server-side templating language that combines HTML with C#. Its output is ordinary HTML. The goal is to keep the template close to the document you want the browser to receive, while giving the compiler enough information to check the data references.
A maintainable view describes presentation. It should not decide who owns a database record or perform a second query to recover missing page data. Put those decisions before rendering, and let the model expose the values the template needs.
Choose your environment
Open TaskBoard.csproj from the example download. Install the ASP.NET and web development workload and a Visual Studio release supporting .NET 10. Select the TaskBoard launch profile and press F5. Use Solution Explorer to find the files named below.
Install the .NET 10 SDK and C# Dev Kit. Open the TaskBoard folder in VS Code. Run dotnet restore, then dotnet run in the integrated terminal. Browse http://localhost:5300. Open files with Ctrl+P (Cmd+P on macOS).
Declare and render a typed model
The @model directive declares the type of Model. Use @ expressions to render scalar values and @foreach to repeat markup. Razor encodes strings by default, so a title containing angle brackets appears as text instead of executing as HTML.
That behavior is a critical boundary. Do not replace encoded output with Html.Raw simply because you want formatting. Rendering untrusted HTML requires a separate, carefully chosen sanitization policy.
@model TaskListPage
<h1>My tasks</h1>
@if (Model.Tasks.Count == 0)
{
<p>No tasks found. Try another search.</p>
}
@foreach (var task in Model.Tasks)
{
<article>
<h2>@task.Title</h2>
<p>@task.Notes</p>
</article>
}Share the document shell
_ViewStart selects _Layout for conventional views. The layout owns the document head, navigation, and footer. RenderBody inserts the current view; an optional Scripts section lets individual pages add behavior without forcing every page to load it.
_ViewImports makes common namespaces and tag helpers available to views beneath it. The Identity area has its own _ViewStart pointing at the same layout so account screens feel like part of the application.
@using TaskBoard.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpersLet tag helpers generate the details
The anchor tag helper generates route-aware links. Form tag helpers generate action URLs and antiforgery inputs for eligible POST forms. Label, input, and validation helpers use model metadata to produce matching field names and validation messages.
These conveniences depend on valid HTML. A button in a form submits by default unless type="button" is set. Give every control an accessible label and avoid placing one form inside another.
<form asp-action="Create" method="post">
<label asp-for="Title"></label>
<input asp-for="Title" />
<span asp-validation-for="Title"></span>
<button type="submit">Save task</button>
</form>Extract repeated presentation carefully
If several pages render the same task summary, a partial view can own that markup. Pass the partial a small, explicit model. A partial is useful for repeated presentation; a view component is a better fit when a reusable UI unit has its own data-loading logic.
Start with the smallest abstraction that removes real duplication. Extracting every few lines into another file can make a simple page harder to navigate. First identify repeated behavior or a stable visual unit, then give it a name.
@model BoardTask
<article class="task">
<h2>@Model.Title</h2>
<p>@(Model.IsComplete ? "Completed" : "To do")</p>
</article>
@* From a parent view: <partial name="_TaskSummary" model="task" /> *@Make it your own.
Create a task with the title <strong>Learn Razor</strong>. Confirm that the task list displays the literal angle brackets, then explain why that is the correct default.
See the solution & reasoning
The title is data, so Razor HTML-encodes it. Executing it as markup would let users change the structure of the page. Keep @task.Title; do not use Html.Raw(task.Title). Rich text is a separate feature with an explicit allowed-format policy.
When something doesn't work
If asp-for or asp-action appears literally in the rendered HTML, check that _ViewImports includes Microsoft.AspNetCore.Mvc.TagHelpers.
A Razor parser error near CSS may come from an @ symbol. In inline Razor markup, escape a literal @ as @@ when needed, or keep styles in a CSS file.
If a layout requests a section that a view does not supply, make the section optional or provide it in that view.
What should you do with a task title supplied by a user?
That's another skill in your toolkit.
Your progress is saved in this browser. Sign in to save completion to your account.