When Microsoft released its version of the MVC (Model, View, Controller) web programming framework, it was revolutionary. It relinquished content control back to the ASP.NET web developers. ASP.NET Web Forms primarily maintained control of the HTML, CSS, and JavaScript required for creating web pages. It worked simply and efficiently for beginning web developers without web programming experience who needed to publish content on the web. As well, Web Forms made up for the varying levels of web programming standards support by the popular web browsers. ASP.NET was released in 2002 when people were primarily using Microsoft Internet Explorer (IE), Netscape Navigator, Opera, and Apple Safari.

However, moving to the MVC programming model didn’t make it easier for transitioning ASP.NET Web Forms developers. All of a sudden they were given complete responsibility for all web content. It’s reminiscent of the famous quote: “With great power comes great responsibility.”

Since its release in 2008, MVC developers cannot use web controls to create web content. Three extensible helper objects were released with MVC (HtmlHelper, AjaxHelper, and UrlHelper) to help generate the web content. These were included to shorten and simplify the code needed for content creation.

As well, additional helper methods can be easily added to these objects via extension methods. The HtmlHelper object is the most powerful of the three, used to create dynamic views and strongly-type views (introduced with MVC 2) with the “For” methods, such as:

@Html.LabelFor(model => model.FirstName)

All server-side code in Razor pages is prefixed with the leading @ sign. The MVC framework does a pretty good job of determining where server-side code ends and client-side code begins.

However, strongly-typed view code has always been a bit of a mess. Quite often, when experienced ASP developers first learn how MVC works, they have commented how it reminds them of the “spaghetti code” they used with ASP script in the late 90s and early 2000s. The views were quite messy with @ signs and confusing lambda operators (=>).

ASP.NET Core MVC New Feature: Tag Helpers

The release of ASP.NET Core 1.0 includes a new feature for the Razor engine called Tag Helpers. Tag Helpers allow the developer to go back to using standard HTML tags in their views while still applying presentation logic written in C# that runs on the web server. Custom tags can also be created – which I demonstrate later.

Tag Helpers can replace the three older helper objects if a developer so chooses. Razor web pages and MVC views are dramatically cleansed of most @ signs, lambdas, and helper methods that return cryptic content. By cryptic content, I am referring to developers guessing and trying to manipulate web content returned by helper methods such as @Html.ValidationSummary().

Tag Helpers markup will appear in Visual Studio as either purple (light VS theme) or teal (dark VS theme). This also applies to custom Tag Helpers.

Advantages of Using ASP.NET Core MVC Tag Helpers

To see the advantages that come with using Tag Helpers, developers must compare the use of these different helper objects. Compare the presentation logic defined in the two following views. The first Create view uses the classic HTML helper methods while the second Create view uses the new Tag Helpers. Notice the amount of code needed and take note of the code with a gray background that represents what will be processed and stripped out before being sent to the web browser.

ASP.NET Core MVC Tag Helper example code 1ASP.NET Core MVC Tag Helper example code 2

Looking at the markup of the first example, it can be confusing for a developer or web page designer to even understand what HTML content will be sent back to the web browser. As well, note how the bootstrap classes are specified. Because “class” is a reserved keyword in C#, we need to prefix it with an @ sign.

Granted, I just used Visual Studio scaffolding to generate the views. However, if I manually add code that used a helper method, I would only get IntelliSense for the property name that I was binding to my helper. I would get no IntelliSense help with the Bootstrap CSS classes, for example. My cursor gets no help typing the Bootstrap class name “col-md-2”.

ASP.NET Core MVC Tag Helper example code 3

Let’s look at the (second) Core Razor version. Very little text has a gray background. I can see the Tag Helpers marked in purple. I have standard HTML tags. I get full IntelliSense for all the attributes, including the aria- attributes for accessibility and the Bootstrap CSS classes names. A web designer would certainly not be scared off of working with this file because it is mostly HTML.

ASP.NET Core MVC Tag Helpers are in purple

The result of using Tag Helpers instead of the other three helper objects is view code that is much more readable and maintainable by anyone who understands basic HTML.

The Tag Helpers that appear on the view are adding functionality to the existing HTML tags. They are considered “additive” because they respect the HTML they have been assigned to and are adding additional functionality. You can even use more than one Tag Helper on a single tag because they are safely additive. The Tag Helpers that are added to existing HTML tags are prefixed with “asp-“. These attributes will be converted to standard HTML before it is ever sent back to the web browser.

Create Your Own Tag Helper

Several Tag Helpers have already been created and are included with ASP.NET Core. You can also create your own custom Tag Helpers. Because ASP.NET Core is completely open-sourced software (OSS), you can inspect some of the available Tag Helpers on GitHub, here:
https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/mvc/views/tag-helpers/intro.md

Tag Helpers have already been created in GitHub

Technically, there are 3 different types of Tag Helpers

  1. An Attribute on a standard HTML tag
  2. A Custom HTML element with optional custom attributes
  3. An invisible Tag Helper (not called out directly in the markup) that automatically affects existing tags or attributes

Let’s now look at the second type of Tag Helper mentioned above. You can easily find an example of a custom tag Helper as a custom element in the shared _layout file. Here is the beginning of _Layout.cshtml:

beginning of _Layout.cshtml for ASP.NET Core MVC Tag Helpers

The <environment> tag is not part of the HTML standard. Instead, it is a custom Tag Helper that will be used to inject the appropriate CSS code. There is another set at the bottom for including JavaScript files.

If the project has been set to “Development” mode, it will use the two CSS files defined locally. If it is being used in “Staging” or “Production”, it will try to use Microsoft’s CDN. The mode is set on the debug tab of the project’s properties, ultimately set in launchSettings.json.

mode set in debug tab

mode set in launchSettings.json

Interestingly, inside the Bootstrap <link> tag for Staging/Production are a couple more Tag Helpers used to fall back to other sources for the CSS file (if necessary). The last one has a Tag Helper called “asp-append-version” which will add a randomized string to the end of the file name to prevent the browser from caching it. This is useful for JavaScript and CSS files that are bring modified locally. It is a real nuisance have to refresh to web browser constantly to get the latest version of a JS/CSS file! If you scroll the bottom of the layout file, you’ll see this technique used again for JavaScript.

It’s incredibly easy to add custom Tag Helpers to your MVC Core project. They can exist in the same web project or in a separate assembly.

There are 3 steps to creating and using a custom Tag Helper

  1. Define the custom Tag Helper
  2. Register (Add) them to the Shared\_ViewImports.cshtml file
  3. Use them in your _layouts, _partials, and views

A common, simple practice for creating and experimenting with custom Tag Helpers is to add them directly to your web project. Usually developers create a dedicated folder for holding custom Tag Helpers such as a \TagHelpers off the root of the website.

add Tag Helpers directly to your web project

Next, we right-click this folder and add a new custom Tag Helper. This tag helper will create a <div> tag with the text reversed, optionally in all uppercase letters. As a best practice, Tag Helper names should always end with “TagHelper”. When you use it, the suffix is dropped automatically – like using custom .NET attributes!

Razor Tag Helper

The starting code is pretty simplistic – that’s good! It keeps it easy to use and configure. The using statement brings along the TagHelper class. Our custom Tag helper inherits from it. By default, the Process() method will be used to return content to the web page that will be rendered and displayed by the web browser.

tag helper example code

The [HtmlTargetElement] attribute is used to set up the rules of when the Tag Helper should be applied. It can be used multiple times to support multiple tags and/or attributes. We’ll be changing that. Not only can we set target elements but also target attributes. This is used by the Razor engine to determine whether or not to use this custom Tag Helper.

If you comment out the attribute, the default behavior is to automatically assume it will be a new tag used in the views. When used, the name of the new tag will have the “TagHelper” suffix dropped. Also, it will be converted to lower Kebab casing. You can assign a different name if you prefer.

Example: ReverseTextDivTagHelper is defined in the view as <reverse-text-div> </reverse-text-div>

Fun Fact: It’s called Kebab casing because it appears the letters have been placed on a skewer – like shish kebab.

By adding public properties to the Tag Helper, you can then configure them in the view as Kebab converted attributes. You will immediately get IntelliSense for these properties in the View editor. Also important, the editor will validate what you are entering based on the data types of the properties. Here is an invalid entry for the Boolean AllCaps property.

configure public properties in the view as Kebab converted attributes

We immediately get a red squiggly line because it is not assigned true or false. Note how the properties were converted to lower kebab case as well. Also observe how we decided to use the badge Bootstrap class.

Here is our completed custom Tag helper that returns a string reversed in a <div> tag, optionally in all capital letters.

namespace CorePubs.TagHelpers
{
  //[HtmlTargetElement(“tag-name”)] // Not using this.
  public class ReverseTextDivTagHelper : TagHelper
  {
    private string _divData = string.Empty;
    public string DivData
    {
      get
      {
        char[] reversedData = _divData.ToCharArray();
        Array.Reverse(reversedData);
        String sDataReversed = new String(reversedData);
        return AllCaps ? sDataReversed.ToUpper() : sDataReversed;
      }
      set { _divData = value; }
    }

    public bool AllCaps { get; set; }

    public override void Process(
      TagHelperContext context, 
      TagHelperOutput output)
    {
      output.TagName = “div”;
      output.Content.SetContent(DivData);
    }
  }
}

The Process() method gives us two very important parameters for creating output. The first parameter, context, gives us access to the control and any contained elements and attributes. This allows us to programmatically manipulate it by adding, modifying, or removing child elements. The second parameter, output, is used to write content back into the webpage.

Before we can use out custom Tag Helper, we must register it in the Shared\_ViewImports.cshtml file with addTagHelper. We will use the asterisk (*) wildcard to include all tags. We must also specify the name of the assembly (not namespace!) that contains the Tag Helpers. Since we added them directly to our web project, the assembly will usually be the same name as the project. Our completed _ViewImports.cshtml file looks like this:

register your custom tag helper

We must correctly configure the About view and test it. Here it is completed.

completed tag helper about view

Here is the result page:

tag helper about page

This looks good. What about the webpage source code? Yes – the content is defined within a <div> tag!

source code for your completed custom tag helper

Don’t Forget!

It’s important to remember that Tag Helpers should only contain presentation logic. Business rules should remain in the model. Tag Helpers simplify the creation of the content being displayed. They can also be bound to objects in strongly-typed views.

As well, these are not a new way to create web controls. There is no event model or view state built into Tag Helpers. Although they are called Tag helpers like the other three helper objects, they behave quite different and give you more power over the outputted content in a more elegant, maintainable fashion.

I hope this information was helpful for you. Best wishes and good luck!  Looking for more ASP.NET content? See our other posts:

Cross-platform ASP.NET Core Debugging with Visual Studio Code