frontend-razor
Apply when working with Razor views, MVC layouts, partial views, and tag helpers
Best use case
frontend-razor is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Apply when working with Razor views, MVC layouts, partial views, and tag helpers
Teams using frontend-razor should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/frontend-razor/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How frontend-razor Compares
| Feature / Agent | frontend-razor | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Apply when working with Razor views, MVC layouts, partial views, and tag helpers
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Razor View Development Patterns
## View Structure
### Project Layout
```
Views/
├── Shared/
│ ├── _Layout.cshtml # Main layout template
│ ├── _LayoutEmpty.cshtml # Minimal layout (no header/footer)
│ ├── _Header.cshtml # Header partial
│ ├── _Footer.cshtml # Footer partial
│ ├── _Navigation.cshtml # Navigation partial
│ ├── _Pagination.cshtml # Reusable pagination
│ └── Components/ # View components
│ └── SearchBox/
│ └── Default.cshtml
├── Home/
│ ├── Index.cshtml
│ └── About.cshtml
├── Products/
│ ├── Index.cshtml
│ ├── Details.cshtml
│ └── _ProductCard.cshtml # Page-specific partial
├── _ViewImports.cshtml # Shared imports and tag helpers
└── _ViewStart.cshtml # Default layout assignment
```
### _ViewImports.cshtml
```cshtml
@using MyApp.Web
@using MyApp.Web.Models
@using MyApp.Core.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyApp.Web
```
### _ViewStart.cshtml
```cshtml
@{
Layout = "_Layout";
}
```
## Layout Templates
### Main Layout
```cshtml
@* Views/Shared/_Layout.cshtml *@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - My Application</title>
@* Head section for page-specific styles *@
@await RenderSectionAsync("Styles", required: false)
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body class="@ViewData["BodyClass"]">
<header>
@await Html.PartialAsync("_Header")
@await Html.PartialAsync("_Navigation")
</header>
<main class="container">
@RenderBody()
</main>
<footer>
@await Html.PartialAsync("_Footer")
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@* Scripts section for page-specific JavaScript *@
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
```
### Page Using Layout
```cshtml
@model ProductListViewModel
@{
ViewData["Title"] = "Products";
ViewData["BodyClass"] = "products-page";
}
@section Styles {
<link rel="stylesheet" href="~/css/products.css" asp-append-version="true" />
}
<h1>@ViewData["Title"]</h1>
<div class="product-grid">
@foreach (var product in Model.Products)
{
@await Html.PartialAsync("_ProductCard", product)
}
</div>
@section Scripts {
<script src="~/js/products.js" asp-append-version="true"></script>
}
```
### Nested Layouts
```cshtml
@* Views/Shared/_LayoutAdmin.cshtml *@
@{
Layout = "_Layout";
}
<div class="admin-container">
<aside class="admin-sidebar">
@await Html.PartialAsync("_AdminNav")
</aside>
<div class="admin-content">
@RenderBody()
</div>
</div>
@section Scripts {
<script src="~/js/admin.js" asp-append-version="true"></script>
@await RenderSectionAsync("AdminScripts", required: false)
}
```
## Partial Views
### Rendering Partials
```cshtml
@* Async (recommended) *@
@await Html.PartialAsync("_ProductCard", product)
@* With explicit view path *@
@await Html.PartialAsync("~/Views/Shared/_Header.cshtml")
@* Synchronous (avoid for I/O operations) *@
@Html.Partial("_Sidebar")
@* As tag helper *@
<partial name="_ProductCard" model="product" />
@* With view-data *@
<partial name="_Pagination" model="Model.Pagination" view-data="ViewData" />
```
### Partial View Example
```cshtml
@* Views/Products/_ProductCard.cshtml *@
@model Product
<article class="product-card">
<a asp-action="Details" asp-route-id="@Model.Id" class="product-card__link">
<img src="@Model.ImageUrl"
alt="@Model.Name"
class="product-card__image" />
<div class="product-card__content">
<h3 class="product-card__title">@Model.Name</h3>
<p class="product-card__price">@Model.Price.ToString("C")</p>
@if (Model.IsOnSale)
{
<span class="product-card__badge">Sale</span>
}
</div>
</a>
</article>
```
## View Components
### Component Class
```csharp
// ViewComponents/RecentArticlesViewComponent.cs
public class RecentArticlesViewComponent : ViewComponent
{
private readonly IArticleService _articleService;
public RecentArticlesViewComponent(IArticleService articleService)
{
_articleService = articleService;
}
public async Task<IViewComponentResult> InvokeAsync(int count = 5)
{
var articles = await _articleService.GetRecentAsync(count);
return View(articles);
}
}
```
### Component View
```cshtml
@* Views/Shared/Components/RecentArticles/Default.cshtml *@
@model IEnumerable<Article>
<section class="recent-articles">
<h3>Recent Articles</h3>
<ul>
@foreach (var article in Model)
{
<li>
<a asp-controller="Articles"
asp-action="Details"
asp-route-slug="@article.Slug">
@article.Title
</a>
<time datetime="@article.PublishedDate.ToString("yyyy-MM-dd")">
@article.PublishedDate.ToString("MMM dd, yyyy")
</time>
</li>
}
</ul>
</section>
```
### Invoking Components
```cshtml
@* Tag helper syntax (preferred) *@
<vc:recent-articles count="5" />
@* Async syntax *@
@await Component.InvokeAsync("RecentArticles", new { count = 5 })
@* With cache *@
<cache expires-after="@TimeSpan.FromMinutes(10)">
<vc:recent-articles count="5" />
</cache>
```
## Tag Helpers
### Built-in Tag Helpers
```cshtml
@* Anchor tag helper *@
<a asp-controller="Products"
asp-action="Details"
asp-route-id="@product.Id"
asp-route-category="@product.Category">
View Details
</a>
@* Form tag helpers *@
<form asp-controller="Contact" asp-action="Submit" method="post">
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Category"></label>
<select asp-for="Category" asp-items="Model.Categories" class="form-control">
<option value="">Select a category</option>
</select>
<span asp-validation-for="Category" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
@* Image with cache busting *@
<img src="~/images/logo.png" asp-append-version="true" alt="Logo" />
@* Environment-specific rendering *@
<environment include="Development">
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
```
### Custom Tag Helper
```csharp
// TagHelpers/EmailTagHelper.cs
[HtmlTargetElement("email")]
public class EmailTagHelper : TagHelper
{
public string Address { get; set; }
public string Subject { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "a";
var href = $"mailto:{Address}";
if (!string.IsNullOrEmpty(Subject))
{
href += $"?subject={Uri.EscapeDataString(Subject)}";
}
output.Attributes.SetAttribute("href", href);
output.Content.SetContent(Address);
}
}
```
```cshtml
@* Usage *@
<email address="support@example.com" subject="Help Request" />
```
## Model Binding
### Strongly-Typed Views
```cshtml
@model ContactFormViewModel
<form asp-action="Submit" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<label asp-for="Name" class="form-label"></label>
<input asp-for="Name" class="form-control" placeholder="Your name" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="form-label"></label>
<input asp-for="Email" class="form-control" type="email" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Message" class="form-label"></label>
<textarea asp-for="Message" class="form-control" rows="5"></textarea>
<span asp-validation-for="Message" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Send Message</button>
</form>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
```
### ViewModel with Validation
```csharp
public class ContactFormViewModel
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100, ErrorMessage = "Name cannot exceed 100 characters")]
[Display(Name = "Full Name")]
public string Name { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Please enter a valid email address")]
public string Email { get; set; }
[Required(ErrorMessage = "Message is required")]
[StringLength(2000, MinimumLength = 10,
ErrorMessage = "Message must be between 10 and 2000 characters")]
public string Message { get; set; }
}
```
## Conditional Rendering
### If/Else Statements
```cshtml
@if (Model.Products.Any())
{
<div class="product-grid">
@foreach (var product in Model.Products)
{
<partial name="_ProductCard" model="product" />
}
</div>
}
else
{
<div class="empty-state">
<p>No products found.</p>
<a asp-action="Index" asp-controller="Home" class="btn btn-link">
Return to Home
</a>
</div>
}
```
### Switch Statements
```cshtml
@switch (Model.Status)
{
case OrderStatus.Pending:
<span class="badge badge-warning">Pending</span>
break;
case OrderStatus.Processing:
<span class="badge badge-info">Processing</span>
break;
case OrderStatus.Shipped:
<span class="badge badge-primary">Shipped</span>
break;
case OrderStatus.Delivered:
<span class="badge badge-success">Delivered</span>
break;
default:
<span class="badge badge-secondary">Unknown</span>
break;
}
```
### Null Checking
```cshtml
@* Null conditional *@
<p>Author: @Model.Author?.Name ?? "Unknown"</p>
@* With fallback *@
@if (Model.ImageUrl != null)
{
<img src="@Model.ImageUrl" alt="@Model.Title" />
}
else
{
<img src="~/images/placeholder.jpg" alt="No image available" />
}
@* Ternary operator *@
<div class="@(Model.IsActive ? "active" : "inactive")">
@Model.Title
</div>
```
## Loops and Collections
### For Loop
```cshtml
@for (int i = 0; i < Model.Items.Count; i++)
{
<div class="item @(i % 2 == 0 ? "even" : "odd")">
<span class="item-number">@(i + 1)</span>
<span class="item-name">@Model.Items[i].Name</span>
</div>
}
```
### Foreach with Index
```cshtml
@{ var index = 0; }
@foreach (var item in Model.Items)
{
<div class="item" data-index="@index">
@item.Name
</div>
index++;
}
```
### Rendering Lists
```cshtml
<ul class="breadcrumb">
@foreach (var crumb in Model.Breadcrumbs)
{
var isLast = crumb == Model.Breadcrumbs.Last();
<li class="breadcrumb-item @(isLast ? "active" : "")">
@if (isLast)
{
@crumb.Title
}
else
{
<a href="@crumb.Url">@crumb.Title</a>
}
</li>
}
</ul>
```
## HTML Helpers vs Tag Helpers
### Prefer Tag Helpers
```cshtml
@* HTML Helper (older approach) *@
@Html.ActionLink("Details", "Details", "Products", new { id = product.Id }, new { @class = "btn btn-link" })
@* Tag Helper (modern, preferred) *@
<a asp-controller="Products" asp-action="Details" asp-route-id="@product.Id" class="btn btn-link">
Details
</a>
@* HTML Helper for form *@
@Html.TextBoxFor(m => m.Name, new { @class = "form-control", placeholder = "Enter name" })
@* Tag Helper (cleaner) *@
<input asp-for="Name" class="form-control" placeholder="Enter name" />
```
### When to Use HTML Helpers
```cshtml
@* Complex dynamic attributes *@
@Html.TextBoxFor(m => m.Name, Model.GetInputAttributes())
@* Raw HTML content *@
@Html.Raw(Model.HtmlContent)
@* Display templates *@
@Html.DisplayFor(m => m.CreatedDate)
@* Editor templates *@
@Html.EditorFor(m => m.Address)
```
## ViewData, ViewBag, and TempData
### ViewData (Dictionary)
```cshtml
@* In Controller *@
ViewData["Title"] = "Product Details";
ViewData["ShowSidebar"] = true;
@* In View *@
<h1>@ViewData["Title"]</h1>
@if ((bool?)ViewData["ShowSidebar"] == true)
{
<partial name="_Sidebar" />
}
```
### ViewBag (Dynamic)
```cshtml
@* In Controller *@
ViewBag.Categories = await _categoryService.GetAllAsync();
@* In View *@
<select asp-items="@(new SelectList(ViewBag.Categories, "Id", "Name"))">
<option value="">All Categories</option>
</select>
```
### TempData (Survives Redirect)
```csharp
// In Controller
TempData["SuccessMessage"] = "Product saved successfully!";
return RedirectToAction("Index");
```
```cshtml
@* In View *@
@if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success alert-dismissible">
@TempData["SuccessMessage"]
<button type="button" class="close" data-dismiss="alert">×</button>
</div>
}
```
## AJAX and Partial Rendering
### AJAX Form Submission
```cshtml
<form id="contact-form" asp-action="SubmitAjax" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div id="result"></div>
@section Scripts {
<script>
$('#contact-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $button = $form.find('button[type="submit"]');
$button.prop('disabled', true).text('Sending...');
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
success: function(response) {
$('#result').html(response);
$form[0].reset();
},
error: function(xhr) {
$('#result').html('<div class="alert alert-danger">An error occurred.</div>');
},
complete: function() {
$button.prop('disabled', false).text('Submit');
}
});
});
</script>
}
```
### Returning Partial Views from Controller
```csharp
[HttpGet]
public async Task<IActionResult> LoadMore(int page)
{
var products = await _productService.GetPagedAsync(page, 10);
return PartialView("_ProductGrid", products);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SubmitAjax(ContactFormViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView("_ContactFormErrors", model);
}
await _contactService.ProcessAsync(model);
return PartialView("_ContactFormSuccess");
}
```
## Accessibility Considerations
```cshtml
@* Semantic HTML with ARIA *@
<nav aria-label="Main navigation">
<ul class="nav" role="menubar">
@foreach (var item in Model.NavItems)
{
<li role="none">
<a href="@item.Url"
role="menuitem"
aria-current="@(item.IsActive ? "page" : null)">
@item.Title
</a>
</li>
}
</ul>
</nav>
@* Form accessibility *@
<div class="form-group">
<label asp-for="Email" id="email-label"></label>
<input asp-for="Email"
aria-labelledby="email-label"
aria-describedby="email-help email-error" />
<small id="email-help" class="form-text text-muted">
We'll never share your email.
</small>
<span asp-validation-for="Email" id="email-error" class="text-danger" role="alert"></span>
</div>
@* Skip link for keyboard navigation *@
<a href="#main-content" class="skip-link">Skip to main content</a>
```Related Skills
ring:dev-refactor-frontend
Analyzes frontend codebase against Ring standards and generates refactoring tasks for ring:dev-cycle-frontend. Dispatches frontend-specific agents in ANALYSIS mode.
rcr-frontend
Component development rules specific to Red Cliff Record. Use when working with React components, Tailwind CSS styling, Radix/Shadcn primitives, icons, buttons, forms, or frontend code in this project. Triggers on component files, styling questions, design tokens, Tailwind v4, Shadcn, Radix, TanStack Forms, Lucide icons, or UI primitive usage patterns (sizing, spacing, layout).
moai-domain-frontend
Frontend development specialist covering React 19, Next.js 16, Vue 3.5, and modern UI/UX patterns with component architecture. Use when building web UIs, implementing components, optimizing frontend performance, or integrating state management.
Frontend Verification & Testing
Verify and test Angular 18 frontend changes using Chrome DevTools MCP. Automatically check console errors, network requests, and visual rendering after implementing tasks or when fixing UI bugs. Use when creating components, debugging visual issues, validating API integration, or ensuring UI requirements are met. File types: .ts, .html, .css, .scss
frontend-trends-2026
Collection of 2026 Frontend Design Formulas (Liquid Glass, Bento, Neo-Brutalism, Eco-Dark).
frontend-react-testing-strategy
Standardized guidelines and patterns for Frontend React Testing Strategy.
Frontend Pages
Create or modify React pages using MUI components, React Router, and the HATEOAS API client.
Frontend Development
พัฒนา Frontend ด้วย Angular, React, Vue, Next.js อย่างมืออาชีพ
frontend-design-ultimate
Create distinctive, production-grade static sites with React, Tailwind CSS, and shadcn/ui — no mockups needed. Generates bold, memorable designs from plain text requirements with anti-AI-slop aesthetics, mobile-first responsive patterns, and single-file bundling. Use when building landing pages, marketing sites, portfolios, dashboards, or any static web UI. Supports both Vite (pure static) and Next.js (Vercel deploy) workflows.
frontend-design
UI/UX design patterns using DaisyUI v5 and TailwindCSS for Splits Network
frontend-design-fixlify
Create distinctive, production-grade frontend interfaces for Fixlify. Automatically activates when building UI components, pages, dashboards, forms, or any visual interface. Uses Fixlify design system with shadcn/ui, Tailwind CSS, and React patterns.
frontend-architect
Build production-grade UI/UX with React (Next.js, Docusaurus), CSS architecture (Tailwind, Modules, Global), animations, theming, performance optimization, state management, and testing. Use when creating React components, building layouts, refactoring CSS, implementing animations, auditing accessibility, optimizing performance, setting up state management, or writing component tests.