What are Razor Pages?
🎯 What You'll Learn
- What Razor Pages are
- Page-focused development
- Setting up Razor Pages
- Folder structure
- Routing conventions
- When to use Razor Pages
What are Razor Pages?
Razor Pages is a page-focused framework for building web UI in ASP.NET Core. It's simpler than MVC for scenarios where you don't need the full separation of concerns.
Razor Pages combines the view and controller logic into a single file, making it easier to organize page-focused scenarios.
Razor Pages vs MVC
| Aspect | Razor Pages | MVC |
|---|---|---|
| Organization | Page-focused | Controller-focused |
| Files | .cshtml + .cshtml.cs | Controller + View |
| Routing | Convention-based (folder structure) | Attribute or route config |
| Use Case | Simple CRUD, forms | Complex apps, APIs |
| Learning Curve | Easier | Steeper |
Setting Up Razor Pages
Create Razor Pages Project
dotnet new webapp -n InvenTrack.Web
cd InvenTrack.Web
dotnet run
Configure Services
var builder = WebApplication.CreateBuilder(args);
// Add Razor Pages services
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// Map Razor Pages
app.MapRazorPages();
app.Run();
Folder Structure
InvenTrack.Web/
├── Pages/
│ ├── Index.cshtml
│ ├── Index.cshtml.cs
│ ├── Products/
│ │ ├── Index.cshtml
│ │ ├── Index.cshtml.cs
│ │ ├── Create.cshtml
│ │ └── Create.cshtml.cs
│ ├── Shared/
│ │ ├── _Layout.cshtml
│ │ └── _ValidationScriptsPartial.cshtml
│ ├── _ViewStart.cshtml
│ └── _ViewImports.cshtml
├── wwwroot/
│ ├── css/
│ ├── js/
│ └── lib/
├── Program.cs
└── appsettings.json
Key Folders
| Folder/File | Purpose |
|---|---|
Pages/ |
Razor Pages (.cshtml + .cshtml.cs) |
.cshtml |
Razor view (HTML + Razor syntax) |
.cshtml.cs |
PageModel (code-behind) |
Pages/Shared/ |
Shared layouts and partials |
wwwroot/ |
Static files |
Creating a Razor Page
1. Create the PageModel (.cshtml.cs)
public class IndexModel : PageModel
{
private readonly InvenTrackDbContext _context;
public IndexModel(InvenTrackDbContext context)
{
_context = context;
}
public List<Product> Products { get; set; } = new();
public async Task OnGetAsync()
{
Products = await _context.Products.ToListAsync();
}
}
2. Create the View (.cshtml)
@page
@model IndexModel
@{
ViewData["Title"] = "Products";
}
<h1>Products</h1>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
@foreach (var product in Model.Products)
{
<tr>
<td>@product.Name</td>
<td>$@product.Price</td>
<td>@product.Quantity</td>
</tr>
}
</tbody>
</table>
The @page directive at the top makes the file a Razor Page.
Without it, it's just a regular Razor view.
Routing Conventions
Razor Pages uses convention-based routing based on folder structure.
| File Path | URL |
|---|---|
Pages/Index.cshtml |
/ or /Index |
Pages/Products/Index.cshtml |
/Products or /Products/Index |
Pages/Products/Create.cshtml |
/Products/Create |
Pages/About.cshtml |
/About |
Custom Routes
@page "/products"
<!-- Now accessible at /products instead of default -->
@page "{id:int}"
<!-- Route parameter: /Products/5 -->
@page "{id:int?}"
<!-- Optional parameter: /Products or /Products/5 -->
Complete InvenTrack Example
public class IndexModel : PageModel
{
private readonly InvenTrackDbContext _context;
public IndexModel(InvenTrackDbContext context)
{
_context = context;
}
public List<Product> Products { get; set; } = new();
public string? SearchTerm { get; set; }
public async Task OnGetAsync(string? search)
{
SearchTerm = search;
var query = _context.Products.AsQueryable();
if (!string.IsNullOrEmpty(search))
{
query = query.Where(p => p.Name.Contains(search));
}
Products = await query.ToListAsync();
}
}
@page
@model IndexModel
@{
ViewData["Title"] = "Products";
}
<h1>InvenTrack Products</h1>
<form method="get">
<div class="input-group mb-3">
<input type="text" name="search" value="@Model.SearchTerm"
class="form-control" placeholder="Search products...">
<button class="btn btn-primary" type="submit">Search</button>
</div>
</form>
<a asp-page="Create" class="btn btn-success mb-3">Add New Product</a>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Quantity</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var product in Model.Products)
{
<tr>
<td>@product.Name</td>
<td>@product.Category</td>
<td>$@product.Price</td>
<td>@product.Quantity</td>
<td>
<a asp-page="Edit" asp-route-id="@product.Id" class="btn btn-sm btn-warning">Edit</a>
</td>
</tr>
}
</tbody>
</table>
When to Use Razor Pages
✅ Good For:
- Simple CRUD apps: Create, Read, Update, Delete operations
- Forms-heavy apps: Contact forms, surveys, data entry
- Page-focused scenarios: Each page is independent
- Beginners: Easier learning curve than MVC
- Small to medium apps: Less boilerplate than MVC
❌ Not Ideal For:
- Complex routing: Custom routing patterns
- RESTful APIs: Use Web API instead
- Shared logic: Multiple pages with same logic
- Large enterprise apps: MVC provides better separation
Best Practices
- @page directive: Always include at the top
- Async handlers: Use OnGetAsync/OnPostAsync
- Dependency injection: Inject services in constructor
- Folder organization: Group related pages in folders
- PageModel properties: Use for data binding
- Naming convention: PageModel class name matches file
Key Takeaways
- Razor Pages: Page-focused framework for web UI
- @page: Directive that makes a file a Razor Page
- PageModel: Code-behind class (.cshtml.cs)
- Convention-based routing: Based on folder structure
- AddRazorPages(): Registers Razor Pages services
- MapRazorPages(): Maps Razor Pages endpoints
- Simpler than MVC: For page-focused scenarios
- Two files: .cshtml (view) + .cshtml.cs (code-behind)
You now understand what Razor Pages are! In the next section, we'll explore the Page Model Pattern—how to structure your code-behind and work with properties and methods.