Razor Pages is a page-based programming model introduced in ASP.NET Core 2.0 to simplify the process of building dynamic, server-side web applications. Itβs built on top of the MVC (Model-View-Controller) pattern but provides a more streamlined, page-centric approachβideal for developers who prefer working with page-focused architectures rather than the traditional controller-action structure.
With Razor Pages, each web page is self-contained, consisting of both the HTML markup and the logic required to handle user input and data. This design makes it easier to build, test, and maintain modern web applications in ASP.NET Core.
What is Razor Pages?
Razor Pages uses the Razor syntax, a lightweight templating engine that combines C# and HTML. Each Razor Page typically has two files:
A .cshtml file β contains HTML markup and Razor syntax.
A .cshtml.cs file β known as the PageModel, containing the C# backend logic for handling page events.
This structure allows developers to keep UI and logic separate but closely related, improving organization and readability.
Example. HelloWorld Razor Page
Index.cshtml
@page
@model RazorPagesDemo.Pages.IndexModel
@{
ViewData["Title"] = "Home Page";
}
<h1>Hello, @Model.Message!</h1>
Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesDemo.Pages
{
public class IndexModel : PageModel
{
public string Message { get; private set; } = "Welcome to Razor Pages!";
public void OnGet()
{
// Initialization logic here
}
}
}
Here:
The @page
directive makes the .cshtml
file a Razor Page.
The IndexModel
class acts as the code-behind, handling page events.
The OnGet()
method executes when the page is accessed via an HTTP GET request.
Project Setup for Razor Pages
Creating a Razor Pages project in Visual Studio or via CLI is simple.
Using .NET CLI
dotnet new webapp -n RazorPagesDemo
cd RazorPagesDemo
dotnet run
This command scaffolds a Razor Pages web application with the required folder structure.
Folder Structure
RazorPagesDemo/
β
βββ Pages/
β βββ Index.cshtml
β βββ Index.cshtml.cs
β βββ Shared/
β βββ _Layout.cshtml
β
βββ wwwroot/
βββ Program.cs
βββ appsettings.json
All Razor Pages reside inside the Pages folder by default, but you can organize them into subfolders for modularity.
How Razor Pages Works
When an HTTP request is made:
The ASP.NET Core Routing middleware maps the URL to a Razor Page.
The pageβs PageModel executes the appropriate handler method (OnGet
, OnPost
, etc.).
The .cshtml view is rendered with the modelβs data.
The final HTML response is sent to the browser.
Unlike MVC, thereβs no need to define separate controllers or route configurationsβthe framework handles it automatically.
Handler Methods in Razor Pages
Each Razor Page can respond to different HTTP verbs using page handler methods:
OnGet()
β Handles HTTP GET requests
OnPost()
β Handles HTTP POST requests
OnPut()
β Handles HTTP PUT requests
OnDelete()
β Handles HTTP DELETE requests
Example. Handling Form Submissions
Contact.cshtml
@page
@model RazorPagesDemo.Pages.ContactModel
<h2>Contact Us</h2>
<form method="post">
<label>Name:</label>
<input type="text" asp-for="Name" />
<br/>
<label>Message:</label>
<textarea asp-for="Message"></textarea>
<br/>
<button type="submit">Submit</button>
</form>
@if (Model.IsSubmitted)
{
<p>Thanks, @Model.Name! Your message was received.</p>
}
Contact.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesDemo.Pages
{
public class ContactModel : PageModel
{
[BindProperty]
public string Name { get; set; }
[BindProperty]
public string Message { get; set; }
public bool IsSubmitted { get; set; } = false;
public void OnGet() { }
public void OnPost()
{
IsSubmitted = true;
}
}
}
The [BindProperty]
attribute binds form inputs directly to C# properties.
OnPost()
executes when the form is submitted.
The view updates dynamically after submission.
Routing in Razor Pages
Routing is automatic in Razor Pages. The path to the .cshtml
file defines the URL route.
Example
However, you can customize routes using the @page
directive:
@page "/custom-route"
You can also define route parameters:
@page "/products/{id:int}"
Details.cshtml.cs
public class DetailsModel : PageModel
{
public int ProductId { get; private set; }
public void OnGet(int id)
{
ProductId = id;
}
}
Visiting /products/10
binds 10
to the id
parameter automatically.
Using Model Binding and Validation
Razor Pages simplifies form handling using model binding and data annotations for validation.
Example. Registration Form
Register.cshtml
@page
@model RazorPagesDemo.Pages.RegisterModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h2>User Registration</h2>
<form method="post">
<div>
<label asp-for="User.Name"></label>
<input asp-for="User.Name" />
<span asp-validation-for="User.Name"></span>
</div>
<div>
<label asp-for="User.Email"></label>
<input asp-for="User.Email" />
<span asp-validation-for="User.Email"></span>
</div>
<button type="submit">Register</button>
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Register.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace RazorPagesDemo.Pages
{
public class RegisterModel : PageModel
{
[BindProperty]
public UserModel User { get; set; }
public string Message { get; private set; }
public void OnGet() { }
public IActionResult OnPost()
{
if (!ModelState.IsValid)
return Page();
Message = $"User {User.Name} registered successfully!";
return RedirectToPage("/Success");
}
public class UserModel
{
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
}
}
}
Here:
The [Required]
and [EmailAddress]
attributes perform server-side validation.
The asp-validation-for
tag helper shows validation messages dynamically.
Client-side validation is automatically enabled using jQuery Unobtrusive Validation.
Dependency Injection in Razor Pages
Razor Pages fully supports dependency injection (DI) through the PageModel
constructor.
Example: Injecting a Service
public interface IMessageService
{
string GetWelcomeMessage();
}
public class MessageService : IMessageService
{
public string GetWelcomeMessage() => "Hello from injected service!";
}
public class IndexModel : PageModel
{
private readonly IMessageService _messageService;
public string Message { get; private set; }
public IndexModel(IMessageService messageService)
{
_messageService = messageService;
}
public void OnGet()
{
Message = _messageService.GetWelcomeMessage();
}
}
In Program.cs:
builder.Services.AddScoped<IMessageService, MessageService>();
This demonstrates how business logic can be separated cleanly from the UI layer.
Layouts and Partial Views
Razor Pages supports Layouts, Partial Views, and Tag Helpers just like MVC.
_Layout.cshtml
defines common site structure (header, footer, navigation).
Partial views like _LoginPartial.cshtml
can be reused across pages.
Example Layout usage
@{
Layout = "_Layout";
}
Razor Syntax Overview
Razor syntax blends C# and HTML seamlessly:
@{
var name = "Dinesh";
var date = DateTime.Now;
}
<p>Hello, @name! Todayβs date is @date.ToShortDateString().</p>
@if (date.DayOfWeek == DayOfWeek.Sunday)
{
<p>Itβs Sunday! Enjoy your weekend!</p>
}
The @
symbol tells the compiler to switch from HTML to C# context, making Razor concise and expressive.
Advantages of Razor Pages
Simplicity β Easier for beginners compared to MVC.
Separation of concerns β UI and backend logic are in separate files.
Better organization β Each page handles its own functionality.
Lightweight and fast β Less boilerplate code than MVC controllers.
Built-in security β Works seamlessly with ASP.NET Core Identity and CSRF protection.
Testability β PageModel classes are easy to test with unit tests.
Real-world Use Cases
Razor Pages is ideal for:
Content-driven websites (blogs, portfolios, company sites)
CRUD applications
Admin dashboards
Internal business tools
Prototypes and small-scale SaaS frontends
Conclusion
Razor Pages in ASP.NET Core represents the evolution of web development in .NET β simpler, cleaner, and more maintainable than traditional MVC for many use cases.
It allows developers to focus on page-level functionality, reduces complexity, and integrates seamlessly with other ASP.NET Core features such as dependency injection, routing, model binding, and validation.
Whether you are a beginner or an experienced developer, Razor Pages provides a powerful and efficient way to build fast, secure, and scalable web applications in the modern .NET ecosystem.