Writing a custom TagHelper in ASP.NET 5

Reading Time: 6 minutes

ASP.NET 5 brings some new features to MVC. One of these features is TagHelpers, which allow us to add new attributes to HTML tags (or create custom tags altogether) to add behavior to these tags. At this moment – beta8 – we receive the following TagHelpers out of the box:

– AnchorTagHelper: allows you to write <a asp-controller=”Home” asp-action=”Index”>Back to home</a> instead of @Html.ActionLink(“Back to home”, “Index”, “Home”)
– LabelTagHelper, InputTagHelper, TextAreaTagHelper, SelectTagHelper, etc.: instead of writing Razor like @Html.LabelFor(m => m.Property1, new { @class = “control-label col-md-2”}), you can use a syntax which is much cleaner: <label asp-for=”Property1″ class=”control-label col-md-2″></label>
– EnvironmentTagHelper: adds a new tag, <environment>, giving you the possibility to include <link> or <script> tags specific for Development or Production environments.
– LinkTagHelper, ScriptTagHelper: adds attributes to <link> and <script> to allow the use of CDN’s and fallback urls.

This list is incomplete, but you can head over to GitHub to see every available TagHelper class currently available.

You can also create your own TagHelper classes. I’ll show you an example which enables you to do this:

That img tag would load a users profile image using the ProfileImage action method in a UsersController class: userful for retrieving a profile image from a blob container on Azure, for example.

To create a custom TagHelper class, start by adding a dependency to the Microsoft.AspNet.Razor.Runtime NuGet package in project.json. You can add this dependency to the top list of global dependencies:

Next, add a new Class to your MVC project. Let’s call this class ImageTagHelper, and let the class derive from the TagHelper class which lives in the Microsoft.AspNet.Razor.Runtime.TagHelpers namespace.

We’re going to add three new attributes to the <img> tag:

  • asp-controller: the MVC Controller
  • asp-action: the action method within the controller
  • asp-route-*: route values for the action method

For each of these attributes, add a class level HtmlTargetElementAttribute:

I’m using constants here to define the attribute names: I can reuse these later when adding the properties which will receive the values of the HTML attributes. Let’s add these properties now:

By using a * at the end of the HtmlTargetElementAttribute, we can map multiple values into an IDictionary<string, string>. Think of this as a way to specify only one property to hold all data-* attributes.

It is now time to add the logic which will make this TagHelper work. Let’s override the Process method:

First of all, we check if the consumer of this custom TagHelper hasn’t set a value for the src attribute, because then the <img> tag would just need to look at the original URI to retrieve the image. We will however show an error when the src attribute and one of our custom attributes has been set together.

If the developer is using our TagHelper without a src attribute, then we’ll fill in the attribute ourselves using _urlHelper.Action(): that’s the @Url.Action method you’ve been using in Razor all along. Of course, we still need to add that _urlHelper instance to our class and inject it. Here is the final version of our TagHelper class code:

But we’re not done, yet. To be able to use custom TagHelper classes, you’ll need to let Razor know about them.  Open the _ViewImports.cshtml file in the Views/Shared folder and add the following line:

This line will tell Razor to load all TagHelper derived classes from the assembly CustomTagHelperDemo. You can also use the name of a specific TagHelper class instead of an asterisk. Also, don’t forget to replace CustomTagHelperDemo with the name of your project or class library.

And now, you can open up a Razor file and tryout the new features you’ve added to the <img> tag: