Beginner 45 min.NET 10

Razor views that stay maintainable

Build typed pages with layouts, tag helpers, encoded output, and reusable presentation.

BY THE END OF THIS TUTORIAL

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
Before you begin

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.

Download the complete companion project
STEP 01

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.

Razor excerpt
@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>
}
STEP 02

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.

Views/_ViewImports.cshtml
@using TaskBoard.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
STEP 03

Let 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.

Razor excerpt
<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>
STEP 04

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.

Views/Shared/_TaskSummary.cshtml — optional addition
@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" /> *@
YOUR TURN

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.

CHECK YOUR UNDERSTANDING

What should you do with a task title supplied by a user?

What should you do with a task title supplied by a user?

ONE STEP FURTHER

That's another skill in your toolkit.

Your progress is saved in this browser. Sign in to save completion to your account.