Tag Helper Authoring in ASP .NET Core 3.1
Tags: dotnet, webdev, csharp
Published: 2020-05-18 15:42:13

---
title: Tag Helper Authoring in ASP .NET Core 3.1
published: true
date: 2020-05-18 14:00:00 UTC
tags: dotnet, webdev, csharp 
canonical_url: https://wakeupandcode.com/tag-helper-authoring-in-asp-net-core-3-1/
---

![](https://wakeupandcode.com/wp-content/uploads/2020/01/aspnetcore-az-banner.png)

This is the twentieth of a new [series of posts](https://wakeupandcode.com/aspnetcore/#aspnetcore2020) on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled  **ASP .NET Core A-Z!**  To differentiate from the [2019 series](https://wakeupandcode.com/aspnetcore/#aspnetcore2019), the 2020 series will mostly focus on a growing single codebase ([NetLearner!](https://wakeupandcode.com/netlearner-on-asp-net-core-3-1/)) instead of new unrelated code snippets week.

Previous post:

- [SignalR in ASP .NET Core 3.1](https://wakeupandcode.com/signalr-in-asp-net-core-3-1/)

**NetLearner on GitHub** :

- Repository: [https://github.com/shahedc/NetLearnerApp](https://github.com/shahedc/NetLearnerApp)
- v0.20-alpha release: [https://github.com/shahedc/NetLearnerApp/releases/tag/v0.20-alpha](https://github.com/shahedc/NetLearnerApp/releases/tag/v0.20-alpha)

**NOTE** : The NetLearner suite of apps doesn’t currently use custom tag helpers in the main branch, so you can check out the new sample code in the experimental subfolder, merged from a [branch](https://github.com/shahedc/NetLearnerApp/tree/TagHelpers/experimental/TagHelperAuthoring):

- Tag Helper Authoring:  [https://github.com/shahedc/NetLearnerApp/tree/TagHelpers/experimental/TagHelperAuthoring](https://github.com/shahedc/NetLearnerApp/tree/TagHelpers/experimental/TagHelperAuthoring) 

# In this Article:

- [T is for Tag Helper Authoring](#T)
- [Custom Tag Helpers](#custom)
- [Setting Attributes and Content](#attrcontent)
- [Updating Pre/Post Content](#prepost)
- [Passing Complex Objects](#objects)
- [Handling Conditions](#conditions)
- [References](#refs)

# T is for Tag Helper Authoring

Tag Helpers are very useful for ASP .NET Core developers in creating HTML elements with server-side attributes. They work equally well in both Razor Pages and MVC views. Better yet, the syntax allows a front-end developer to easily customize the UI, with HTML/CSS knowledge.

If you need a refresher on built-in tag helpers in ASP .NET Core, you may revisit an earlier post in this series:

- Forms and Fields in ASP .NET Core: [https://wakeupandcode.com/forms-and-fields-in-asp-net-core-3-1/](https://wakeupandcode.com/forms-and-fields-in-asp-net-core-3-1/)

![Tag Helpers in ASP .NET Core](https://wakeupandcode.com/wp-content/uploads/2020/05/taghelper-diagram-1024x427.png)<figcaption>Tag Helpers in ASP .NET Core</figcaption>

Authoring your own tag helpers is as easy as implementing the ITagHelper interface. To make things easier, the TagHelper class (which already implements the aforementioned interface) can be extended to build your custom tag helpers.

# Custom Tag Helpers

As with most concepts introduced in ASP .NET Core, it helps to use named folders and conventions to ease the development process. In the case of Tag Helpers, you should start with a “TagHelpers” folder at the root-level of your project for your convenience. You can save your custom tag helper classes in this folder.

This blog post and its corresponding code sample builds upon the [official tutorial for authoring tag helpers](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring). While the official tutorial was originally written to cover instructions for MVC views, this blog post takes a look at a Razor Page example. The creation of Tag Helpers involves the same process in either case. Let’s start with the _synchronous_ and _asynchronous_ versions of a Tag Helper that formats email addresses.

The class [EmailTagHelper.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/EmailTagHelper.cs) defines a tag helper that is a subclass of the [TagHelper class](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.razor.taghelpers.taghelper), saved in the “TagHelpers” folder. It contains a  **Process** () method that changes the output of the HTML tag it is generating.

```
public class **EmailTagHelper** : TagHelper{ ... _**// synchronous method, CANNOT call output.GetChildContentAsync();**_ public override void **Process** (TagHelperContext context, TagHelperOutput output) { // ... } } 
```

The class [AsyncEmailTagHelper.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/AsyncEmailTagHelper.cs) defines a tag helper that is also a subclass of the [TagHelper class](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.razor.taghelpers.taghelper), saved in the aforementioned “TagHelpers” folder. It contains a  **ProcessAsync** () method, which has a different signature (returns Task object instead of void) and grabs the child content from the output using output.GetChildContentAsync();

```
public class **AsyncEmailTagHelper** : TagHelper{ ... _**// ASYNC method, REQUIRED to call output.GetChildContentAsync();**_ public override async Task **ProcessAsync** (TagHelperContext context, TagHelperOutput output) { // ... }}
```

In order to use the tag helper in a Razor Page, simply add a using statement for the Tag Helper’s namespace, and then include a custom HTML tag that has the same name as the Tag Helper’s class name (without the TagHelper suffix). For the Email and AsyncEmail Tag Helpers, the corresponding tags in your Razor Page would be <email> and <async-email> respectively.

In the [EmailTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/EmailTester.cshtml) page:

```
< **email** mail-to="Black.Widow"></ **email** >
```

In the [AsyncEmailTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/AsyncEmailTester.cshtml) page:

```
< **async-email** >Black.Widow</ **async-email** >
```

Note that the PascalCase capitalization in the class name corresponds to a lowercase tag in kebab-case. In a browser, the end result includes a clickable email link from the Razor Pages. Both the non-async and async version of the methods produce similar end results.

![](https://wakeupandcode.com/wp-content/uploads/2020/05/taghelper-email-1024x638.png)<figcaption>Email tag helper in a browser</figcaption>

![Async Email tag helper in a browser](https://wakeupandcode.com/wp-content/uploads/2020/05/taghelper-asyncemail-1024x638.png)<figcaption>Async Email tag helper in a browser </figcaption>

# Setting Attributes and Content

So how does the  **Process** () method convert your custom tags into valid HTML tags? It does that in a series of steps.

1. Set the HTML element as the tag name to replace it with, e.g. <a>
2. Set each attribute within that HTML element, e.g. href
3. Set HTML Content _within _the tags.

The process involved is slightly different between the synchronous and asynchronous versions of the Process method. In the synchronous [EmailTagHelper.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/EmailTagHelper.cs) class, the  **Process** () method does the following:

```
_ **// 1. Set the HTML element** _output. **TagName** = "a"; _ **// 2. Set the href attribute** _output.Attributes. **SetAttribute** ("href", "mailto:" + address);_ **// 3. Set HTML Content** _output.Content. **SetContent** (address);
```

In the asynchronous AsyncEmailTagHelper.cs class, the  **ProcessAsync** () method does the following:

```
_ **// 1. Set the HTML element** _output. **TagName** = "a"; var **content** = await **output**.GetChildContentAsync();var **target** = **content**.GetContent() + "@" + EmailDomain;_ **// 2. Set the href attribute within that HTML element, e.g. href** _output.Attributes. **SetAttribute** ("href", "mailto:" + **target** );_ **// 3. Set HTML Content** _output.Content. **SetContent** (target);
```

The difference between the two is that the async method gets the output content _asynchronously_ with some additional steps. Before setting the attribute in Step 2, it grabs the output content from  **GetChildContentAsync** () and then uses  **content**. **GetContent** () to extract the content before setting the attribute with **output.Attributes.SetAttribute(). **

# Updating Pre/Post Content

This section recaps the  **BoldTagHelper**  explained in the [docs tutorial](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring), by consolidating all the lessons learned. In the [BoldTagHelper.cs class](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/BoldTagHelper.cs) from the sample, you can see the following code:

```
[**HtmlTargetElement** ("bold")][**HtmlTargetElement** (Attributes = "bold")]public class **BoldTagHelper** : TagHelper{ public override void **Process** (TagHelperContext context, TagHelperOutput output) { output.Attributes. **RemoveAll** ("bold"); output. **PreContent**. **SetHtmlContent** ("<strong>"); output. **PostContent**. **SetHtmlContent** ("</strong>"); }}
```

Let’s go over what the code does, line by line:

- The [**HtmlTargetElement**] attribute forces a Tag Helper to target a specific element, e.g. [**HtmlTargetElement** (“bold”)], which will target a < **bold** > tag in a Razor Page or MVC View.
- When one or more attributes are specified, e.g. [**HtmlTargetElement** ( **Attributes**  = “bold”)], the Tag Helper targets a  **bold**  _attribute_ within an element, e.g. < **p**   **bold** >
- Combining the above one after the other gives you an OR condition, in which either scenario can be matched.
- Combining them in a single [**HtmlTargetElement**] creates an AND condition, which would match a bold tag with a bold attribute, which is not very useful, e.g. [**HtmlTargetElement** (“bold”, Attributes = “bold”)]

Here is a snippet the corresponding Razor Page for testing the above scenario, [BoldTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/BoldTester.cshtml):

```
< **p bold** >This paragraph uses a P tag with a bold attribute. Using a tag helper, the entire paragraph should be displayed with bold text.</ **p** >< **bold** >This statement uses a custom bold tag to be displayed in bold.</ **bold** >
```

The tag helper affects both fragments, as seen in the screenshot below:

![Bold tag helper in a browser](https://wakeupandcode.com/wp-content/uploads/2020/05/taghelper-bold-1024x638.png)<figcaption>Bold tag helper in a browser </figcaption>

The statements in the  **Process** () method accomplish the following:

- The  **RemoveAll** () method from  **output**. **Attributes**  removes the “bold” attribute within the tag, as it is essentially acting as a placeholder.
- The **SetHtmlContent()** from  **output.PreContent ** adds an _opening_ < **strong** > tag  inside the enclosing element, i.e. just after <p> or <bold>
- The **SetHtmlContent()** from  **output.Postontent ** adds a _closing_ </ **strong** > tag  insidethe enclosing element, i.e. just before </p> or </bold>

# Passing Complex Objects

What if you want to pass a more complex object, with properties and objects within it? This can be done by defining a C# model class, e.g. [SuperheroModel.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Models/SuperheroModel.cs), that can be initialized inside in the Page Model class ([SuperheroInfoTester.cshtml.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml.cs)) and then used in a Razor Page ([SuperheroInfoTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml)). The tag helper ([SuperheroTagHelper.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/SuperheroTagHelper.cs)) then brings it all together by replacing < **superhero** > tags with whatever  **SuperHeroModel**  info is passed in.

Let’s take a look at all its parts, and how it all comes together.

**Object Model** : [SuperheroModel.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Models/SuperheroModel.cs)

```
public class **SuperheroModel** { public string LastName { get; set; } public string FirstName { get; set; } public string SuperName { get; set; } public bool HasSurvived { get; set; } public bool ShowInfoWithSpoilers { get; set; }}
```

**Razor Page** : [SuperheroInfoTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml)

```
@ **page** @ **model** SuperheroInfoTesterModel... <h3>Black Widow Info:</h3><div condition="@Model.blackWidowInfo.ShowInfoWithSpoilers"> < **superhero** hero-info="Model.blackWidowInfo" /></div>...
```

**Page Model for Razor Page** : [SuperheroInfoTester.cshtml.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml.cs)

```
public class **SuperheroInfoTesterModel** : PageModel{ public **SuperheroModel** blackWidowInfo { get; set; } // ... public void **OnGet** () { blackWidowInfo = new **SuperheroModel** { // ... } // ... }}
```

**Superhero Tag Helper** : [SuperheroTagHelper.cs](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/TagHelpers/SuperheroTagHelper.cs)

```
public class **SuperheroTagHelper** : TagHelper{ public **SuperheroModel**  **HeroInfo** { get; set; } public override void **Process** (TagHelperContext context, TagHelperOutput output) { // ... }}
```

Going through the above code:

1. The tag helper is named  **SuperheroTagHelper** , implying that it can be used for < **superhero** > tags in a Razor Page, e.g. [SuperHeroInfoTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml)
2. The tag helper also contains a  **SuperheroModel**  object called  **HeroInfo** , which allows a hero-info attribute, i.e. < **superhero ** hero-info=”Model.property”>
3. The  **SuperheroModel**  class contains various public properties that provide information about a specific superhero.
4. The  **SuperHeroInfoTesterModel**  page model class includes an  **OnGet** () method that initializes multiple  **SuperheroModel**  objects, to be displayed in the Razor Page.

Inside the tag helper, the Process() method takes care of replacing the < **superhero** > tag with a < **section** > tag:

```
public override void **Process** (TagHelperContext context, TagHelperOutput output){ string **htmlContent** = $@"<ul><li><strong>First Name:</strong> { **HeroInfo.FirstName** }</li><li><strong>Last Name:</strong> { **HeroInfo.LastName** }</li><li><strong>Superhero Name:</strong> { **HeroInfo.SuperName** }</li><li><strong>Survived Endgame? </strong> { **HeroInfo.HasSurvived** }</li></ul>"; output. **TagName** = "section"; output. **Content**. **SetHtmlContent** (htmlContent); output. **TagMode** = TagMode. **StartTagAndEndTag** ; }
```

After initializing some HTML content to display a < **ul** > list, the above code in the  **Process** () method accomplishes the following:

1. Set the HTML element as the tag name to replace it with, e.g. <section>
2. Set HTML Content _within _the tags.
3. Set Tag Mode to include both start and end tags, e.g. <section> … </section>

**End Result in Browser** :

![Superhero tag helper in a browser](https://wakeupandcode.com/wp-content/uploads/2020/05/taghelper-superhero-1024x638.png)<figcaption>Superhero tag helper in a browser </figcaption>

In a web browser, you can see that that the < **superhero** > tag has been converted into a < **section** > tag with < **ul** > content.

# Handling Conditions

When you want to handle a UI element in different ways based on certain conditions, you may use a ConditionTagHelper. In this case, a condition is used to determine whether spoilers for the popular movie Avengers: Endgame should be displayed or not. If the spoiler flag is set to false, the character’s info is not displayed at all.

```
@ **page** @ **model** SuperheroInfoTesterModel...< **div condition** ="@ **Model**. **blackWidowInfo**. **ShowInfoWithSpoilers**"> <superhero hero-info="Model.blackWidowInfo" /></div>...
```

In the above code from the [SuperheroInfoTester.cshtml](https://github.com/shahedc/NetLearnerApp/blob/master/experimental/TagHelperAuthoring/Pages/SuperheroInfoTester.cshtml) page:

- the < **div** > includes a condition that evaluates a boolean value, e.g. Model.blackWidowInfo.ShowInfoWithSpoilers
- the Model object comes from the @ **model**  defined at the top of the page
- the boolean value of  **ShowInfoWithSpoilers ** determines whether the < **div** > is displayed or not.

# References

- Tag Helpers in ASP.NET Core: [https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro)
- Tag Helpers in forms in ASP.NET Core: [https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms)
- Author Tag Helpers in ASP.NET Core: [https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/authoring)