The community is working on translating this tutorial into Italian, but it seems that no one has started the translation process for this article yet. If you can help us, then please click "More info".

Tag Helpers:


In this article, I'll be going through a bit of ASP/ASP.NET history and tell you how Microsoft came up with the concept of Tag helpers. In case you just want to see how they are used and you don't care too much about the journey that led to Tag Helpers, you may chose to skip to the Summary in the bottom of the article and then move on to the next articles where we'll see how the various Tag helpers work.

When Microsoft replaced ASP Classic with what we today refer to as ASP.NET WebForms, they introduced the concept of controls to the world of coding for the web. This was a well-known concept when creating applications for the desktop, but not for the web. Controls allowed you to get a lot of out-of-the-box functionality by writing tags which were then interpreted by ASP.NET and turned into regular HTML and CSS before reaching the browser.

From markup to server controls

Microsoft basically implemented server controls for all the common HTML controls, like the asp:TextBox (which would be rendered as an INPUT or TEXTAREA HTML tag) or the asp:Label. However, they also went beyond that with controls like the asp:DataGrid, which would take a lot of parameters and options and then be rendered as a complete TABLE with columns and rows based on the configuration and the data source.

Unfortunately, the concept of the ASP.NET WebForms server controls never felt completely natural for all developers. The server controls were initially created to save time and code by allowing you to quickly implement even advanced functionality with few lines of markup. But instead of saving time, you would often find your self writing MORE code, to work around odd cases which the server controls didn't fully support.

Another major complaint about the ASP.NET WebForms server control was the lack of control over the resulting HTML - by abstracting away the actual HTML into server controls, ASP.NET WebForms would then be in charge of generating the output to the browser and this became a problem for two reasons:

First of all, sometimes the resulting HTML would not work in all browsers. Back in the days when ASP.NET WebForms was the at its peak, there were big differences in how various browsers would respond to the HTML and CSS you provided it with. Since you didn't control the HTML and CSS generated, you could run into rendering issues which now became almost impossible to fix.

Second of all, a lot of purists out there demanded the HTML to be formatted in a specific way, even though the browser didn't care. As an example, you can write HTML tags in UPPERCASE or lowercase, you can use single-quotes or double-quotes for attribute/values and so on. The browser doesn't care, but a lot of programmers are used to case-sensitive programming languages and their eyes hurt when the HTML they wrote wasn't formatted the same way as the HTML generated by the server controls.

From server controls to HTML helpers

For this reason, Microsoft decided to completely leave out the server controls when they implemented ASP.NET MVC. They had listened to the developers and the developers wanted full control of the generated markup, even if they had to write it all themselves. For this, they invented the Razor language (already explained in detail in this tutorial), which allowed you to combine programming logic like IF statements and loops with regular markup.

People were generally happy with this new approach, but something was lacking. Microsoft always strives to make things easier for developers and right now, too much markup and Razor code was written, without the ability to connect the Model data with its visual representation in the View. For this reason, they introduced HTML Helpers.

The concept of HTML Helpers allowed you to generate markup for your Model data, especially when generating FORM's and its elements. For instance, if you were to create an INPUT field for the Title property of your Model, you would write markup like this:

<input type="text" name="Title" value="@Model.Title" />

With the introduction of HTML Helpers, you could instead call the TextBoxFor() method on the Html helper class, to have this tag generated automatically:

@Html.TextBoxFor(model => model.Title)

It's shorter and you get the added benefit of compile-time checking of the Razor code - if the Title property suddenly stops existing, e.g. because you renamed it, the compiler will complain about it.

From HTML helpers to Tag helpers

Thanks to HTML helpers, binding your Model data together with the markup in your Views is now a lot easier, but a problem from the days of server controls still exists: Since the markup has been abstracted into helper methods, these helper methods are now responsible for generating your HTML and they also have to be flexible enough to support all the ways you might want to use them. Consider our example from before, but now imagine that we want to add a couple of properties with values to the HTML generated:

@Html.TextBoxFor(model => model.Title, new { @class = "form-control", style = "font-weight: bold; font-size: 120%;"  })

The example is now way less elegant than it was before, just because we wanted to add a class and style property with values to the INPUT tag. To some developers, this approach never felt natural either - they didn't like the mix of C# code in the Razor syntax with inline CSS or even JavaScript. Especially in large forms, the constant mix of regular HTML tags and Razor calls to HTML Helper methods could look really messy.

Microsoft decided to solve this problem by taking the best from both worlds: With Tag helpers, you write regular HTML tags instead of Razor commands, but you can mix in custom server-side attributes which will allow you to, for instance, easily bind to data from your model. With Tag helpers, we can rewrite our previous example to something like this:

<input asp-for="Title" class="form-control" style="font-weight: bold; font-size: 120%;" />

Notice that the ONLY thing that doesn't look like a regular HTML tag/attribute setup is the asp-for attribute. This is how ASP.NET MVC knows which property from the Model that this control is created for - this information will be used to generate the Name of the INPUT field ("Title", in this case) and the value will be automatically added as well. An id attribute will also be generated for the control, but you are free to override this value by adding your own id attribute and value!

Tag Helpers vs. HTML Helpers

With all of the above in mind, one thing is clear: The ASP.NET platform, as most other platforms, is under constant development. New concepts are constantly added, while older ones might be deprecated. However, as of writing this, HTML helpers are still relevant and a viable choice in ASP.NET MVC. You may prefer their syntax or the way they work, but on top of that, there are still things they can do, which Tag helpers can't do (yet).

For this reason, you will also see both Tag helpers and HTML helpers being used through out this tutorial. And that's fine - use whichever approach you find best for the task at hand. After all, both Tag helpers and HTML helpers are syntactical sugar: they help you accomplish a mundane task with less keystrokes.


ASP.NET MVC Tag helpers will aid you in the generation of markup for your Views, especially when the purpose is to bind data from your Models to controls/markup in your View. Unlike previous approaches, like server controls and HTML Helpers, you don't have to shift to another language to connect the Model and View and you remain in control of the markup generated. In other words, Tag helpers will make a lot of things easier for you when you create your Views. In the following articles, we'll go through all the built-in Tag helpers so that you can see how powerful and yet easy-to-use they are.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!