Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions aspnetcore/mvc/views/tag-helpers/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: tdykstra
description: Learn what Tag Helpers are and how to use them in ASP.NET Core.
ms.author: tdykstra
ms.custom: H1Hack27Feb2017, sfi-image-nochange
ms.date: 03/18/2019
ms.date: 05/04/2026
uid: mvc/views/tag-helpers/intro
---
# Tag Helpers in ASP.NET Core
Expand All @@ -14,21 +14,21 @@ By [Rick Anderson](https://twitter.com/RickAndMSFT)
## What are Tag Helpers

Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files. For example, the built-in `ImageTagHelper` can append a version number to the image name. Whenever the image changes, the server generates a new unique version for the image, so clients are guaranteed to get the current image (instead of a stale cached image). There are many built-in Tag Helpers for common tasks - such as creating forms, links, loading assets and more - and even more available in public GitHub repositories and as NuGet packages. Tag Helpers are authored in C#, and they target HTML elements based on element name, attribute name, or parent tag. For example, the built-in `LabelTagHelper` can target the HTML `<label>` element when the
`LabelTagHelper` attributes are applied. If you're familiar with HTML Helpers, Tag Helpers reduce the explicit transitions between HTML and C# in Razor views. In many cases, HTML Helpers provide an alternative approach to a specific Tag Helper, but it's important to recognize that Tag Helpers don't replace HTML Helpers and there's not a Tag Helper for each HTML Helper. [Tag Helpers compared to HTML Helpers](#tag-helpers-compared-to-html-helpers) explains the differences in more detail.
`LabelTagHelper` attributes are applied. If you're familiar with HTML Helpers, Tag Helpers reduce the explicit transitions between HTML and C# in Razor views. In many cases, HTML Helpers provide an alternative approach to a specific Tag Helper, but it's important to recognize that Tag Helpers don't replace HTML Helpers. There's not a Tag Helper for each HTML Helper. [Tag Helpers compared to HTML Helpers](#tag-helpers-compared-to-html-helpers) explains the differences in more detail.

Tag Helpers aren't supported in Razor components. For more information, see <xref:blazor/components/index#tag-helpers-arent-supported-in-components>.

## What Tag Helpers provide

**An HTML-friendly development experience**
### An HTML-friendly development experience

For the most part, Razor markup using Tag Helpers looks like standard HTML. Front-end designers conversant with HTML/CSS/JavaScript can edit Razor without learning C# Razor syntax.
Usually, Razor markup using Tag Helpers looks like standard HTML. Front-end designers conversant with HTML/CSS/JavaScript can edit Razor without learning C# Razor syntax.

**A rich IntelliSense environment for creating HTML and Razor markup**
### A rich IntelliSense environment for creating HTML and Razor markup

This is in sharp contrast to HTML Helpers, the previous approach to server-side creation of markup in Razor views. [Tag Helpers compared to HTML Helpers](#tag-helpers-compared-to-html-helpers) explains the differences in more detail. [IntelliSense support for Tag Helpers](#intellisense-support-for-tag-helpers) explains the IntelliSense environment. Even developers experienced with Razor C# syntax are more productive using Tag Helpers than writing C# Razor markup.

**A way to make you more productive and able to produce more robust, reliable, and maintainable code using information only available on the server**
### A way to make you more productive and able to produce more robust, reliable, and maintainable code using information only available on the server

For example, historically the mantra on updating images was to change the name of the image when you change the image. Images should be aggressively cached for performance reasons, and unless you change the name of an image, you risk clients getting a stale copy. Historically, after an image was edited, the name had to be changed and each reference to the image in the web app needed to be updated. Not only is this very labor intensive, it's also error prone (you could miss a reference, accidentally enter the wrong string, etc.) The built-in `ImageTagHelper` can do this for you automatically. The `ImageTagHelper` can append a version number to the image name, so whenever the image changes, the server automatically generates a new unique version for the image. Clients are guaranteed to get the current image. This robustness and labor savings comes essentially free by using the `ImageTagHelper`.

Expand Down Expand Up @@ -71,7 +71,7 @@ If you create a new ASP.NET Core web app named *AuthoringTagHelpers*, the follow

[!code-cshtml[](../../../mvc/views/tag-helpers/authoring/sample/AuthoringTagHelpers/src/AuthoringTagHelpers/Views/_ViewImportsCopy.cshtml?highlight=2&range=2-3)]

The `@addTagHelper` directive makes Tag Helpers available to the view. In this case, the view file is `Pages/_ViewImports.cshtml`, which by default is inherited by all files in the *Pages* folder and subfolders; making Tag Helpers available. The code above uses the wildcard syntax ("\*") to specify that all Tag Helpers in the specified assembly (*Microsoft.AspNetCore.Mvc.TagHelpers*) will be available to every view file in the *Views* directory or subdirectory. The first parameter after `@addTagHelper` specifies the Tag Helpers to load (we are using "\*" for all Tag Helpers), and the second parameter "Microsoft.AspNetCore.Mvc.TagHelpers" specifies the assembly containing the Tag Helpers. *Microsoft.AspNetCore.Mvc.TagHelpers* is the assembly for the built-in ASP.NET Core Tag Helpers.
The `@addTagHelper` directive makes Tag Helpers available to the view. In this case, the view file is `Pages/_ViewImports.cshtml`, which by default is inherited by all files in the *Pages* folder and subfolders; making Tag Helpers available. The preceding code uses the wildcard syntax ("\*") to specify that all Tag Helpers in the specified assembly (*Microsoft.AspNetCore.Mvc.TagHelpers*) will be available to every view file in the *Views* directory or subdirectory. The first parameter after `@addTagHelper` specifies the Tag Helpers to load (we're using "\*" for all Tag Helpers), and the second parameter "Microsoft.AspNetCore.Mvc.TagHelpers" specifies the assembly containing the Tag Helpers. *Microsoft.AspNetCore.Mvc.TagHelpers* is the assembly for the built-in ASP.NET Core Tag Helpers.

To expose all of the Tag Helpers in this project (which creates an assembly named *AuthoringTagHelpers*), you would use the following:

Expand Down Expand Up @@ -102,7 +102,7 @@ The `@removeTagHelper` has the same two parameters as `@addTagHelper`, and it re

### Controlling Tag Helper scope with the `_ViewImports.cshtml` file

You can add a `_ViewImports.cshtml` to any view folder, and the view engine applies the directives from both that file and the `Views/_ViewImports.cshtml` file. If you added an empty `Views/Home/_ViewImports.cshtml` file for the *Home* views, there would be no change because the `_ViewImports.cshtml` file is additive. Any `@addTagHelper` directives you add to the `Views/Home/_ViewImports.cshtml` file (that are not in the default `Views/_ViewImports.cshtml` file) would expose those Tag Helpers to views only in the *Home* folder.
You can add a `_ViewImports.cshtml` to any view folder, and the view engine applies the directives from both that file and the `Views/_ViewImports.cshtml` file. If you added an empty `Views/Home/_ViewImports.cshtml` file for the *Home* views, there would be no change because the `_ViewImports.cshtml` file is additive. Any `@addTagHelper` directives you add to the `Views/Home/_ViewImports.cshtml` file (that aren't in the default `Views/_ViewImports.cshtml` file) would expose those Tag Helpers to views only in the *Home* folder.

<a name="opt-out"></a>

Expand All @@ -126,19 +126,19 @@ The `@tagHelperPrefix` directive allows you to specify a tag prefix string to en
@tagHelperPrefix th:
```

In the code image below, the Tag Helper prefix is set to `th:`, so only those elements using the prefix `th:` support Tag Helpers (Tag Helper-enabled elements have a distinctive font). The `<label>` and `<input>` elements have the Tag Helper prefix and are Tag Helper-enabled, while the `<span>` element doesn't.
In the code image that follows, the Tag Helper prefix is set to `th:`, so only those elements using the prefix `th:` support Tag Helpers (Tag Helper-enabled elements have a distinctive font). The `<label>` and `<input>` elements have the Tag Helper prefix and are Tag Helper-enabled, while the `<span>` element doesn't.

![Razor markup with Tag Helper prefix set to "th:" for a label and an input element name](intro/_static/thp.png)

The same hierarchy rules that apply to `@addTagHelper` also apply to `@tagHelperPrefix`.

## Self-closing Tag Helpers

Many Tag Helpers can't be used as self-closing tags. Some Tag Helpers are designed to be self-closing tags. Using a Tag Helper that was not designed to be self-closing suppresses the rendered output. Self-closing a Tag Helper results in a self-closing tag in the rendered output. For more information, see [this note](xref:mvc/views/tag-helpers/authoring#self-closing) in [Authoring Tag Helpers](xref:mvc/views/tag-helpers/authoring).
Many Tag Helpers can't be used as self-closing tags. Some Tag Helpers are designed to be self-closing tags. Using a Tag Helper that wasn't designed to be self-closing suppresses the rendered output. Self-closing a Tag Helper results in a self-closing tag in the rendered output. For more information, see [this note](xref:mvc/views/tag-helpers/authoring#self-closing) in [Authoring Tag Helpers](xref:mvc/views/tag-helpers/authoring).

## C# in Tag Helpers attribute/declaration

Tag Helpers do not allow C# in the element's attribute or tag declaration area. For example, the following code is not valid:
Tag Helpers don't allow C# in the element's attribute or tag declaration area. For example, the following code isn't valid:

```cshtml
<input asp-for="LastName"
Expand Down Expand Up @@ -203,11 +203,11 @@ IntelliSense statement completion allows you to enter the tab key to complete th

![The user has typed an opening bracket, the HTML element name "label", and begins to type an attribute name ("as"). IntelliSense presents a dialog of suggestions with "asp-for" selected.](intro/_static/stmtcomplete.png)

As soon as a Tag Helper attribute is entered, the tag and attribute fonts change. Using the default Visual Studio "Blue" or "Light" color theme, the font is bold purple. If you're using the "Dark" theme the font is bold teal. The images in this document were taken using the default theme.
As soon as a Tag Helper attribute is entered, the tag and attribute fonts change. Using the default Visual Studio "Blue" or "Light" color theme, the font is bold purple. If you're using the "Dark" theme, the font is bold teal. The images in this document were taken using the default theme.

![The user selected "asp-for", which is now in bold purple because the user isn't using the Dark theme.](intro/_static/labelaspfor2.png)

You can enter the Visual Studio *CompleteWord* shortcut (Ctrl +spacebar is the [default](/visualstudio/ide/default-keyboard-shortcuts-in-visual-studio)) inside the double quotes (""), and you are now in C#, just like you would be in a C# class. IntelliSense displays all the methods and properties on the page model. The methods and properties are available because the property type is `ModelExpression`. In the image below, I'm editing the `Register` view, so the `RegisterViewModel` is available.
You can enter the Visual Studio *CompleteWord* shortcut (Ctrl +spacebar is the [default](/visualstudio/ide/default-keyboard-shortcuts-in-visual-studio)) inside the double quotes (""), and you're now in C#, just like you would be in a C# class. IntelliSense displays all the methods and properties on the page model. The methods and properties are available because the property type is `ModelExpression`. In the following image, I'm editing the `Register` view, so the `RegisterViewModel` is available.

![The user types "e" in the value for the "asp-for" attribute. IntelliSense suggests possible completion with "Email" selected.](intro/_static/intellemail.png)

Expand Down Expand Up @@ -271,7 +271,7 @@ The Visual Studio editor helps you write **all** of the markup in the Tag Helper

## Tag Helpers compared to Web Server Controls

* Tag Helpers don't own the element they're associated with; they simply participate in the rendering of the element and content. ASP.NET Web Server Controls are declared and invoked on a page.
* Tag Helpers don't own the element they're associated with; they participate in the rendering of the element and content. ASP.NET Web Server Controls are declared and invoked on a page.

* ASP.NET Web Server Controls have a non-trivial lifecycle that can make developing and debugging difficult.

Expand Down
40 changes: 20 additions & 20 deletions aspnetcore/mvc/views/view-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ author: tdykstra
description: Learn how view components are used in ASP.NET Core and how to add them to apps.
ms.author: tdykstra
ms.custom: mvc
ms.date: 04/20/2022
ms.date: 05/04/2026
monikerRange: '>= aspnetcore-3.1'
uid: mvc/views/view-components
---
Expand All @@ -16,7 +16,7 @@ By [Rick Anderson](https://twitter.com/RickAndMSFT)

## View components

View components are similar to partial views, but they're much more powerful. View components don't use model binding, they depend on the data passed when calling the view component. This article was written using controllers and views, but view components work with [Razor Pages](https://www.learnrazorpages.com/razor-pages/view-components).
View components are similar to partial views, but they're much more powerful. View components don't use model binding. They depend on the data passed when calling the view component. This article was written using controllers and views, but view components work with [Razor Pages](https://www.learnrazorpages.com/razor-pages/view-components).

A view component:

Expand All @@ -27,17 +27,17 @@ A view component:

View components are intended anywhere reusable rendering logic that's too complex for a partial view, such as:

* Dynamic navigation menus
* Tag cloud, where it queries the database
* Sign in panel
* Shopping cart
* Recently published articles
* Sidebar content on a blog
* A sign in panel that would be rendered on every page and show either the links to sign out or sign in, depending on the sign in state of the user
* Dynamic navigation menus.
* Tag cloud, where it queries the database.
* Sign in panel.
* Shopping cart.
* Recently published articles.
* Sidebar content on a blog.
* A sign in panel that would be rendered on every page and show either the links to sign out or sign in, depending on the sign in state of the user.

A view component consists of two parts:

* The class, typically derived from <xref:Microsoft.AspNetCore.Mvc.ViewComponent>
* The class, typically derived from <xref:Microsoft.AspNetCore.Mvc.ViewComponent>.
* The result it returns, typically a view.

Like controllers, a view component can be a POCO, but most developers take advantage of the methods and properties available by deriving from <xref:Microsoft.AspNetCore.Mvc.ViewComponent>.
Expand All @@ -50,18 +50,18 @@ This section contains the high-level requirements to create a view component. La

### The view component class

A view component class can be created by any of the following:
Any of the following can create a view component class:

* Deriving from <xref:Microsoft.AspNetCore.Mvc.ViewComponent>
* Decorating a class with the [`[ViewComponent]`](xref:Microsoft.AspNetCore.Mvc.ViewComponentAttribute) attribute, or deriving from a class with the `[ViewComponent]` attribute
* Creating a class where the name ends with the suffix [`ViewComponent`](xref:Microsoft.AspNetCore.Mvc.ViewComponents)
* Deriving from <xref:Microsoft.AspNetCore.Mvc.ViewComponent>.
* Decorating a class with the [`[ViewComponent]`](xref:Microsoft.AspNetCore.Mvc.ViewComponentAttribute) attribute, or deriving from a class with the `[ViewComponent]` attribute.
* Creating a class where the name ends with the suffix [`ViewComponent`](xref:Microsoft.AspNetCore.Mvc.ViewComponents).

Like controllers, view components must be public, non-nested, and non-abstract classes. The view component name is the class name with the `ViewComponent` suffix removed. It can also be explicitly specified using the <xref:Microsoft.AspNetCore.Mvc.ViewComponentAttribute.Name%2A> property.

A view component class:

* Supports constructor [dependency injection](../../fundamentals/dependency-injection.md)
* Doesn't take part in the controller lifecycle, therefore [filters](../controllers/filters.md) can't be used in a view component
* Supports constructor [dependency injection](../../fundamentals/dependency-injection.md).
* Doesn't take part in the controller lifecycle, therefore [filters](../controllers/filters.md) can't be used in a view component.

To prevent a class that has a case-insensitive `ViewComponent` suffix from being treated as a view component, decorate the class with the [`[NonViewComponent]`](xref:Microsoft.AspNetCore.Mvc.NonViewComponentAttribute) attribute:

Expand Down Expand Up @@ -124,7 +124,7 @@ A View Component can be invoked as a [Tag Helper](xref:mvc/views/tag-helpers/int

[!code-cshtml[](view-components/sample6.x/ViewComponentSample/Views/ToDo/IndexTagHelper.cshtml?name=snippet&highlight=8-9)]

Pascal-cased class and method parameters for Tag Helpers are translated into their [kebab case](https://stackoverflow.com/questions/11273282/whats-the-name-for-dash-separated-case/12273101). The Tag Helper to invoke a view component uses the `<vc></vc>` element. The view component is specified as follows:
Pascal-cased class and method parameters for Tag Helpers are translated into their [kebab case](https://stackoverflow.com/questions/11273282/whats-the-name-for-hyphen-separated-case/12273101#12273101). The Tag Helper to invoke a view component uses the `<vc></vc>` element. The view component is specified as follows:

```cshtml
<vc:[view-component-name]
Expand Down Expand Up @@ -157,7 +157,7 @@ In the following example, the view component is called directly from the control

## Create a basic view component

[Download](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/mvc/views/view-components/sample6.x), build and test the starter code. It's a basic project with a `ToDo` controller that displays a list of *ToDo* items.
[Download](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/mvc/views/view-components/sample6.x), build, and test the starter code. It's a basic project with a `ToDo` controller that displays a list of *ToDo* items.

![List of ToDos](view-components/_static/2dos.png)

Expand Down Expand Up @@ -185,8 +185,8 @@ Notes on the code:
```

* The `[ViewComponent]` attribute in the preceding code tells the view component selector to use:
* The name `PriorityList` when looking for the views associated with the component
* The string "PriorityList" when referencing the class component from a view.
* The name `PriorityList` when looking for the views associated with the component.
* The string "PriorityList" when referencing the class component from a view.
* The component uses [dependency injection](../../fundamentals/dependency-injection.md) to make the data context available.
* `InvokeAsync` exposes a method that can be called from a view, and it can take an arbitrary number of arguments.
* The `InvokeAsync` method returns the set of `ToDo` items that satisfy the `isDone` and `maxPriority` parameters.
Expand Down
Loading
Loading