Comparing TagHelpers with View Components
The past couple of blogs have focused on Custom TagHelpers and ViewComponents. Each concept was introduced with .Net Core and both help simplify and clean up the way existing Razor Views are constructed.
Similarities
Even though TagHelpers and ViewComponents are their own independent implementation, they have some points in common.
Dependency Injection
ViewComponents is more well known for being the business logic piece between the two, but both still allow other classes to be injected.
TagHelper Dependency Injection:
public class ProductTagHelper : TagHelper { private readonly IProductService _productService; public ProductTagHelper(IProductService productService) { _productService = productService; } }
ViewComponent Dependency Injection:
public class ProductViewComponent : ViewComponent { private readonly IProductService _productService; public ProductViewComponent(IProductService productService) { _productService = productService; } }
Add HTML
ViewComponents don't alter a Razor View's HTML as it just adds to it. TagHelpers can add new HTML as well as alter HTML
Before: HTML calling a built-in .Net TagHelper
![HTML calling a built-in .Net TagHelper](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/html-calling-taghelper-before.webp)
After: HTML calling a built-in .Net TagHelper
![HTML calling a built-in .Net TagHelper](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/html-calling-taghelper-after.webp)
Before: Razor syntax calling a ViewComponent
![Razor syntax calling a ViewComponent](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/razor-syntax-calling-viewcomponent.webp)
After: HTML after ViewComponent rendered
![HTML after ViewComponent rendered](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/html-after-viewcomponent-rendered.webp)
Can be called with simple HTML, no Razor Syntax needed
To keep things simple, both TagHelpers and ViewComponents can be called using current HTML features. No Razor Syntax is needed. ViewComponents need to have the "vc:" prefix and the element name as a kebob of the ViewComponent name. TagHelpers can be either their own element or an attribute on an existing HTML element.
TagHelper being called as its own element
![TagHelper being called as its own element](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/taghelper-as-own-element.webp)
TagHelper being called as an attribute of an existing element
![TagHelper being called as an attribute of an existing element](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/taghelper-as-attribute.webp)
ViewComponent being called using C# razor syntax
![ViewComponent being called using C# razor syntax](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-as-razor-syntax.webp)
ViewComponent being called like a TagHelper
![ViewComponent being called like a TagHelper](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-called-as-taghelper.webp)
Note: If you are using .Net Core 1.0, this similarity will not exist as calling ViewComponents by HTML element structure is not supported. In that case (and as well still in .Net Core 1.1), you will need to use Razor View syntax.
Sync and Async Methods
TagHelpers and ViewComponents both have the ability to complete their work synchronously or asynchronously
TagHelper Methods
![TagHelper Methods](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/taghelper-methods.webp)
ViewComponent being called like a TagHelper
![ViewComponent Methods](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-methods.webp)
Differences
Of course, there would be no need for different implementations if TagHelpers and ViewComponents didn't have their differences. Based upon these differences, you can choose which is the best implementation for you and what you are trying to accomplish.
TagHelpers
No lifecycle and no ViewState. Unlike when you call a controller's action method or ViewComponent, TagHelpers don't really have a lifecycle. When a razor view runs, any TagHelper get compiled into the output and the HTML is returned. It's as simple as that.
HTML Tags and Attributes
TagHelpers can be called by using new or existing HTML elements as well as utilizing attributes. By decorating a TagHelper class with the appropriate TargetElement attributes or RestrictChildren attributes, there can be more than a single way for a TagHelper to be called
![HTML Tags and Attributes](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/html-tags-and-attribute.webp)
Built into .Net Core
.Net Core comes with built-in TagHelpers already created, so for a good amount of functionality, you don't have to create anything new.
Form TagHelper: This TagHelper will even create an anti-forgery token
-
As Entered:
-
As Rendered:
Select TagHelper: This TagHelper will populate all your dropdown options
![Select TagHelper](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/select-taghelper.webp)
-
As Entered:
-
As Rendered:
Anchor TagHelper: This TagHelper will set your href
-
As Entered:
-
As Rendered:
Environment TagHelper: This TagHelper will display the content within its tags when you are running your application in the environment specified
-
As Entered:
-
As Rendered:
Note: These are just a few of the many built-in TagHelpers. For a comprehensive look into all of them, you should take a peek at them on GitHub
No Nesting but Multiples allowed
Since TagHelpers return HTML, they cannot new-up another TagHelper call (ie. return new HTML with a TagHelper call that was not already on the original Razor View). This example shows a "my-paragraph" TagHelper returning HTML with a "strong" TagHelper, but the strong TagHelper is not rendered. I've included a real "strong" TagHelper to show that it does work
-
As Entered:
-
As Rendered:
A single HTML element can have multiple TagHelpers. Since TagHelpers run synchronously on the same element and the result of the first can impact the result of the second, you should specify the order they are to be constructed.
![Multiple TagHelpers Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/multiple-taghelpers-code.webp)
-
As Entered:
-
As Rendered:
Existing Attributes and Child Content
Regardless of if you are using a TagHelper defined HTML element or an existing HTML element, you can grab and utilize the attributes on the element as well as the child content of the element.
![Existing Attributes and Child Content](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/existing-attributes-and-child-content.webp)
![Existing Attributes and Child Content Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/existing-attributes-and-child-content-code.webp)
You can also specify content to be added before or after the child content.
![Existing Attributes and Child Content Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/existing-attributes-and-child-content-code-2.webp)
You can even suppress the html from even showing based upon particular parameters.
![Existing Attributes and Child Content Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/existing-attributes-and-child-content-code-3.webp)
![Existing Attributes and Child Content Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/existing-attributes-and-child-content-code-4.webp)
No Templates
TagHelpers don't allow you to use another Razor View and plug data into it. Instead data must be constructed pretty much through string implementation. This can get hairy if you are trying to create a large chunk of view. Therefore, I feel that TagHelpers are best suited for small sets of HTML.
![No Template Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/no-templates-code.webp)
If you feel you have to compose a large chunk of complex HTML, the best approach is to break it up, let TagHelpers construct small chunks of HTML, and then let a "wrapper" TagHelper put it all together
ViewComponents
Nesting allowed
Since ViewComponents go through a full Razor View lifecycle of being compiled, a Razor View can call a ViewComponent whose own Razor View can call another ViewComponent. Also, with inheriting from ViewComponent, your ViewComponent is being given the majority of ViewContext you would be given through a regular MVC Controller call
-
The Controller View:
-
The Widget ViewComponent View:
-
The HelloWorld ViewComponent View:
-
Rendered View:
Data View Size
ViewComponents typically compose larger chunks of data than TagHelpers. As we've seen though, multiple TagHelpers together can compose large chunks of HTML as well. Therefore, along with the typical size of data rendered, ViewComponents are a typical place for business logic to be performed.
Run-time errors
ViewComponents can cause run-time errors, unlike TagHelpers which can exist on a Razor View even if they don't exist.
![ViewComponent Runtime Error](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-runtime-error.webp)
There are exceptions to this rule.
-
You can call your ViewComponent as a TagHelper. If the ViewComponent doesn't exist, then nothing is rendered.
-
You can also call your ViewComponent as a Type instead of a string. This way if the ViewComponent doesn't exist you will get a compile time error.
Where to Invoke ViewComponents
Unlike TagHelpers which are called strictly from the RazorView, ViewComponents can be called both from a RazorView, a controller's action method, or a WebApi Method
![Where to Invoke ViewComponents](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/invoke-viewcomponent.webp)
Same call, different results
Given a ViewComponent that is called with the exact same inputs, the exact same result will not occur every time. This result is due to how views are searched for. Views are search first by "Views\{ControllerName}\Components\{ComponentName}\Default.cshtml" and then by "Views\Shared\Components\{ComponentName}\Default.cshtml". Whichever is found first, is used. Therefore, if you have two different controller Index.cshtml views and call the same ViewComponent with the exact same parameters, you can possibly get different results
The ViewComponent that is called:
![ViewComponent Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-code.webp)
Folder Structure of where ViewComponent views:
![ViewComponent Folder Structure](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-folder-structure.webp)
Call to ProductItemViewComponent from ProductController's razor view and rendered html:
![ViewComponent Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-code-2.webp)
![ViewComponent Result](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-result.webp)
Call to ProductItemViewComponent from CartController's razor view and rendered html:
![ViewComponent Code](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-code-3.webp)
![ViewComponent Result](/images/blog-posts/technical-posts/learn-aspnet-core/taghelpers-viewcomponents-comparison/viewcomponent-result-2.webp)
Summary
Overall which concept to use depends not only on what you are trying to do but also who is trying to do it. For someone who comes from a C#, .Net background, ViewComponents are probably the preferred path as they incorporate a lot more business logic and look like previous Child Actions. Whereas someone who is more UI centric will prefer TagHelper as he might need to know a little C# code, but doesn't need to know all the code that is necessary for ViewComponents.
Another way to determine which item to use is based upon the reusability and flexibility to make changes to the rendered output. ViewComponents can be looked at as once you render some HTML it is rendered. TagHelpers on the other hand encapsulate small bits of view logic and ensure consistent generation of HTML across the site. This can be viewed as putting a layer of HTML on top of another layer. If you want to add another layer, you just add that additional TagHelper to the appropriate element.