ASP.NET Core MVC Mastery
Lesson 28 of 58 48% of course

Custom HTML Helpers

20 · 8 min · 5/23/2026

Sign in to track progress and bookmarks.

Custom HTML Helpers — Extending Razor with Reusable UI Logic

What if you need a button, badge, or alert component that appears across dozens of views with consistent styling? Instead of copy-pasting HTML, you create a Custom HTML Helper — a reusable C# method that generates HTML markup and is callable from any Razor view using @Html.YourMethod().

1. WHAT Are Custom HTML Helpers?

Custom HTML Helpers are static extension methods on the IHtmlHelper interface. They encapsulate repetitive HTML generation logic into reusable, testable C# methods. When called in a Razor view, they return IHtmlContent — properly encoded HTML that the Razor engine safely renders.

The Building Blocks
  • IHtmlHelper — The interface your extension method extends
  • TagBuilder — The class that generates well-formed, XSS-safe HTML tags
  • IHtmlContent — The return type that Razor renders without double-encoding
  • _ViewImports.cshtml — Where you register your helper's namespace

2. HOW to Create a Custom HTML Helper

Step-by-Step: Building a Reusable Status Badge Helper

// Helpers/HtmlHelperExtensions.cs
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MyApp.Helpers
{
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Renders a Bootstrap-styled status badge.
        /// Usage: @Html.StatusBadge("Active", "success")
        /// </summary>
        public static IHtmlContent StatusBadge(this IHtmlHelper helper,
            string text, string variant = "primary")
        {
            // TagBuilder ensures proper HTML encoding (XSS prevention)
            var span = new TagBuilder("span");
            span.AddCssClass($"badge bg-{variant} rounded-pill px-3 py-2");
            span.InnerHtml.Append(text);

            return span;
        }

        /// <summary>
        /// Renders a delete confirmation button with data attributes.
        /// Usage: @Html.DeleteButton("/api/products/5", "Delete Product")
        /// </summary>
        public static IHtmlContent DeleteButton(this IHtmlHelper helper,
            string deleteUrl, string confirmMessage = "Are you sure?")
        {
            var button = new TagBuilder("button");
            button.AddCssClass("btn btn-danger btn-sm");
            button.Attributes.Add("data-delete-url", deleteUrl);
            button.Attributes.Add("data-confirm", confirmMessage);
            button.Attributes.Add("onclick", "confirmDelete(this)");
            button.InnerHtml.AppendHtml("Delete");

            return button;
        }

        /// <summary>
        /// Renders a formatted price with currency symbol.
        /// Usage: @Html.FormatPrice(49.99m, "USD")
        /// </summary>
        public static IHtmlContent FormatPrice(this IHtmlHelper helper,
            decimal price, string currency = "USD")
        {
            var symbol = currency switch
            {
                "USD" => "$", "EUR" => "€", "GBP" => "£",
                "INR" => "₹", "JPY" => "¥", _ => currency
            };

            var span = new TagBuilder("span");
            span.AddCssClass("fw-bold text-success");
            span.InnerHtml.AppendHtml($"{symbol}{price:N2}");

            return span;
        }
    }
}

Register the Namespace

<!-- Views/_ViewImports.cshtml -->
@using MyApp.Helpers     <!-- Makes @Html.StatusBadge(), etc. available in all views -->

Use in Razor Views

<!-- Clean, reusable usage across any view -->
<h3>Order Status: @Html.StatusBadge(order.Status, order.IsActive ? "success" : "danger")</h3>

<td>@Html.FormatPrice(product.Price, "USD")</td>
<td>@Html.DeleteButton("/api/products/" + product.Id, "Delete " + product.Name + "?")</td>

3. Advanced: Helper Returning Complex HTML

/// Renders a Bootstrap card with header, body, and footer
public static IHtmlContent InfoCard(this IHtmlHelper helper,
    string title, string body, string footer = null, string icon = "fas fa-info-circle")
{
    var card = new TagBuilder("div");
    card.AddCssClass("card shadow-sm mb-4");

    // Card Header
    var header = new TagBuilder("div");
    header.AddCssClass("card-header bg-primary text-white");
    header.InnerHtml.AppendHtml($"{title}");
    card.InnerHtml.AppendHtml(header);

    // Card Body
    var cardBody = new TagBuilder("div");
    cardBody.AddCssClass("card-body");
    cardBody.InnerHtml.AppendHtml($"

{body}

"); card.InnerHtml.AppendHtml(cardBody); // Optional Footer if (!string.IsNullOrEmpty(footer)) { var cardFooter = new TagBuilder("div"); cardFooter.AddCssClass("card-footer text-muted small"); cardFooter.InnerHtml.Append(footer); card.InnerHtml.AppendHtml(cardFooter); } return card; }

4. When to Use HTML Helpers vs Tag Helpers vs View Components

FeatureCustom HTML HelpersTag HelpersView Components
Syntax@Html.Method()<tag asp-attr="">@await Component.InvokeAsync()
ReturnsIHtmlContentModifies TagHelperOutputRazor View (.cshtml)
DI Support❌ Static methods✅ Constructor injection✅ Full DI
Best ForSmall, stateless UI snippetsEnhancing HTML elementsComplex components with data access
ComplexityLowMediumHigh

5. Best Practices

  • Always use TagBuilder — never concatenate raw HTML strings (XSS risk)
  • Return IHtmlContent, not string — prevents double-encoding
  • Keep helpers stateless — no database calls, no service injection
  • Use View Components when you need async data access or complex business logic
  • Register namespace in _ViewImports.cshtml for global availability

6. Interview Mastery

Q: "How would you decide between creating a Custom HTML Helper vs a Tag Helper vs a View Component?"

Answer: "The decision comes down to complexity and data needs. For simple, stateless UI elements like badges, icons, or formatted prices — I use HTML Helpers. They're just static methods, fast to write, and have zero overhead. When I need to enhance existing HTML elements with server-side behavior — like adding asp-for binding — I use Tag Helpers because they feel natural in HTML. When the component needs its own data (e.g., a 'Recent Posts' sidebar that queries the database) — I use View Components, since they support full DI and have their own controller-like InvokeAsync method with a dedicated Razor view."

Test your knowledge

Quizzes linked to this course—pass to earn certificates.

Browse all quizzes
ASP.NET Core MVC Mastery

On this page

1. WHAT Are Custom HTML Helpers? 2. HOW to Create a Custom HTML Helper 3. Advanced: Helper Returning Complex HTML 4. When to Use HTML Helpers vs Tag Helpers vs View Components 5. Best Practices 6. Interview Mastery
1. Core Framework
Introduction to ASP.NET Core MVC
MODULE 1: INTRODUCTION & ENVIRONMENT SETUP
Microsoft Web Stack Overview Evolution of ASP.NET Environment Setup
2. View Engine
Layouts & Partial Views in Razor
MODULE 2: .NET CORE FUNDAMENTALS
Core Concepts Project Structure Startup Flow Middleware Pipeline
MODULE 3: ASP.NET CORE BASICS
Creating Project CLI Commands wwwroot & Static Files
MODULE 4: MVC FUNDAMENTALS
MVC Architecture Dependency Injection (DI) Service Lifetimes
MODULE 5: DATA PASSING TECHNIQUES
ViewData vs ViewBag TempData ViewModel Pattern
MODULE 6: ROUTING
Conventional vs Attribute Routing Custom Constraints
MODULE 7: VIEWS & UI
Razor View Engine Layouts & Sections View Components
MODULE 8: ACTION RESULTS
ViewResult JsonResult RedirectResult
MODULE 9: HTML HELPERS
Form Helpers Custom HTML Helpers
MODULE 10: TAG HELPERS
Built-in Tag Helpers Custom Tag Helpers
MODULE 11: MODEL BINDING
FromQuery vs FromRoute Complex Binding
MODULE 12: VALIDATION
Data Annotations Remote Validation Fluent Validation
MODULE 13: STATE MANAGEMENT
Cookies & Sessions TempData
MODULE 14: FILTERS & SECURITY
Action Filters Authorize Filters Anti-forgery
MODULE 15: ENTITY FRAMEWORK CORE (DEEP DIVE)
DbContext Migrations LINQ Relationships
MODULE 16: DESIGN PATTERNS
Repository Pattern Unit of Work Clean Architecture
MODULE 17: FILE HANDLING
File Upload/Download PDF/Excel Generation
MODULE 18: ADVANCED ASP.NET CORE
Request Lifecycle Bundling & Minification Deployment
MODULE 19: PERFORMANCE & BEST PRACTICES
Caching Strategies Async Programming Secure Coding
MODULE 20: RAZOR PAGES (BONUS)
Razor Pages vs MVC
MODULE 21: REAL-WORLD PROJECTS (🔥 MUST DO)
E-Commerce Web Application Employee Management System