---
title: TemplateDescriptor and tags
slug: cms-11/dev-docs/templatedescriptor-and-tags
docTags: 
createdAt: 2026-07-22T19:45:21.967Z
---

`TemplateDescriptor` lets you add metadata when registering templates for content types, and tags can be used, for example, to control the rendering of objects in a content area.

:::hint{type="info"}
**Note**
The examples here are based on MVC.
:::

## TemplateDescriptor attribute

A page or a block can have multiple associated templates, for example, one for a web channel and one for a mobile device. Pages can also have a *partial template* used, for example, when you display the page inside the content area of another page.

`TemplateDescriptor` is an optional attribute you can use to register templates and is involved when the system decides which template to use when rendering a content instance in a specific context. See [Render content](docId:1j5tkfTPZbGw5lRhAh4Xx)  and  [Select templates](docId\:X6jWKJqMhlD1rFhW_y6u6) for information on how templates are registered and selected. Use the attribute to add metadata such as template path, inheritance, content type model, and description. You can also define a default template for all content types.

### Properties

The attribute is in the `EPiServer.Framework.DataAnnotations` namespace and some of its properties are described below.

- `Path` – Default: null – Set the path to the rendering template only if the folder structure **does not** follow the namespace structure. A namespace convention searches for the file in the path according to the namespace.
- `ModelType` – Default: null – The model for the content type. Set this for **untyped** templates. It derives from `EPiServer.TemplatePage` to set the model type, which is the type of the `CurrentPage` property.
- `Default` – Default: false – Defines the template as the default template for the model type.
- `Description` – Default: null – Contains a description of the template.
- `Inherited` – Default: false – When set to **true**, model types that inherit from the **ModelType** get the template as a supported template.

### Behavior

- If `TemplateDescriptor` is not present, template inheritance is true by default.
- If `TemplateDescriptor` is present but without specified parameters, the default values specified above will apply.
- If `TemplateDescriptor` is present with `inherited` set to true, the template is inherited by page types based on the associated page type, which is useful if you need a fallback template for content types without specific templates.

### Examples

### Default template

The template (controller) for an `Article` page type, with the `TemplateDescriptor` attribute present. `Inherited=false`, meaning that this is the default template for the `ArticlePage` page type and no inheritance will take place.

```csharp
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using EPiServer;
using EPiServer.Core;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.Mvc;
using MyEpiserverSite.Models.Pages;
using System.Web.Security;

namespace MyEpiserverSite.Controllers {
  [TemplateDescriptor(
    Inherited = false,
    Default = true,
    Description = "Default template to be used by Article pages")]

  public class ArticlePageController: PageControllerBase<ArticlePage> {

    public ActionResult Index(ArticlePage currentPage) {
      // Implementation of action view the page. 

      return View(currentPage);
    }
  }
}
```

### Partial page rendering

The following example shows how to use `TemplateDescriptor` for defining a partial page renderer when rendering a page inside a content area.

Assume you have the following `SitePageData` base class with a summary (`MetaKeywords` string) and an image:

```csharp
namespace MyEpiserverSite.Models.Pages {
  public abstract class SitePageData: EPiServer.Core.PageData {
    [Display(GroupName = "SEO", Order = 10, Name = "Summary", Description = "Add a short content summary.")]
    public virtual String MetaKeywords {
      get;
      set;
    }

    [Display(
      Name = "Image", Description = "Add an image representing the content (optional)",
      GroupName = "SEO",
      Order = 20)]
    public virtual ContentReference Image {
      get;
      set;
    }
  }
}
```

You have a partial page controller decorated with `TemplateDescriptor` with `Inherited=true`, to render page partials for pages inheriting from `SitePageData`. The controller selects a view in a folder specified by the namespace convention.

```csharp
namespace MyEpiserverSite.Controllers {
  [TemplateDescriptor(Inherited = true)]
  public class PagePartialController: PartialContentController<SitePageData> {
    //
    // GET: /PagePartial/
    public override ActionResult Index(SitePageData currentContent) {
      return PartialView("/Views/Shared/PagePartials/PagePartial.cshtml", currentContent);
    }
  }
}
```

The **partial view** has an if-else construct checking if the model has a template for the content. Any page inheriting from `SitePageData`, will now be rendered displaying the page name with a link, the `MetaKeywords` string, and an image when added to the content area of another page.

```csharp
@using EPiServer.Core
    @model MyEpiserverSite.Models.Pages.SitePageData
    
    <div class="block span2">
      <div class="border">
        @if (Model.HasTemplate())
          {
            <a href="@Url.PageUrl(Model.LinkURL)">
              <h3>@Model.PageName</h3>
              <p>@Html.PropertyFor(x => x.MetaKeywords)<p>
              <img src="@Url.ContentUrl(Model.Image)" />
            </a>
          }
          else
            {
              <h3>@Model.PageName</h3>
            }
      </div>
    </div>
```

### Pages without controllers

An example of how you can handle page types that do not have their specific controllers by `specifyinenherited=true`, and dynamically selecting template. See the [Optimizely CMS (Alloy) sample site](docId\:NhghXxLS4Yd0f2K0aHJsC) for a fully working sample.

```csharp
[TemplateDescriptor(Inherited = true)]
public class DefaultPageController: PageControllerBase<SitePageData> {
  public ViewResult Index(SitePageData currentPage) {
    var model = CreateModel(currentPage);
    return View(string.Format("~/Views/{0}/Index.cshtml", currentPage.GetOriginalType().Name), model);
  }

  /// <summary>
  /// Creates a PageViewModel where the type parameter is the type of the page.
  /// </summary>
  /// <remarks>
  /// Used to create models of a specific type without the calling method having to know that type.
  /// </remarks>
  private static IPageViewModel<SitePageData> CreateModel(SitePageData page) {
    var type = typeof (PageViewModel<>).MakeGenericType(page.GetOriginalType());
    return Activator.CreateInstance(type, page) as IPageViewModel<SitePageData>;
  }
}
```

## Use tags

You can apply *tags* to be used in the rendering selection. When a template is associated with a tag, then that template is used only when the calling context, such as `Property` or `PropertyFor` in a view, has a matching tag. You can also have different content areas render the same content in different ways using tags. If you have active [display channels](docId\:DOH7by8bAqGeC4FnaZciq) for your content types, the `ChannelName` will act as a tag in the rendering selection.

### Examples

### Register multiple templates

Assume you have a model containing a teaser block with a heading and an image.

```csharp
namespace MyEpiserverSite.Models.Blocks {
  [ContentType]
  public class TeaserBlock: BlockData {
    public virtual String Heading {
      get;
      set;
    }
    public virtual ContentReference Image {
      get;
      set;
    }
  }
}
```

- Register two templates for the teaser block to display differently depending on the context.
- Add two sidebar templates (left and right) to display the block in the sidebar area of web pages with this.
- Add register a template for the standard block, which is part of our content model. The blocks are displayed in the content area of our web site's start page.

For performance reasons, you should use *partial views directly* for block types and not controllers. See [Block types and templates](docId\:QGqRtYIyYGfG4XAb2yxlo). Because you have partial views without controllers here, use `EPiServer.Web.Mvc.IViewTemplateModelRegistrator` to register your templates. In the Business folder of your project, create a `ViewTemplateModelRegistrator` class inheriting from `IViewTemplateModelRegistrator`, and add the desired templates and tags.

The following code shows the template registration class using `IViewTemplateModelRegistrator`:

```csharp
namespace MyEpiserverSite.Business {
  public class ViewTemplateModelRegistrator: IViewTemplateModelRegistrator {
    public void Register(TemplateModelCollection viewTemplateModelRegistrator) {
      viewTemplateModelRegistrator.Add(typeof (TeaserBlock),
        new EPiServer.DataAbstraction.TemplateModel() {
          Name = "SidebarTeaserRight",
            Description = "Displays a teaser for a page.",
            Path = "~/Views/Shared/SidebarTeaserBlockRight.cshtml",
            AvailableWithoutTag = true
        },
        new EPiServer.DataAbstraction.TemplateModel() {
          Name = "SidebarTeaserLeft",
            Description = "Displays a teaser for a page.",
            Path = "~/Views/Shared/SidebarTeaserBlockLeft.cshtml",
            Tags = new string[] {
              RenderingTags.Sidebar
            }
        });

      viewTemplateModelRegistrator.Add(typeof (StandardBlock),
        new EPiServer.DataAbstraction.TemplateModel() {
          Name = "SidebarTeaser",
            Description = "Displays a teaser of a page.",
            Path = "~/Views/Shared/StandardBlock.cshtml",
            Tags = new string[] {
              RenderingTags.Sidebar
            }
        });
    }
  }
}
```

The following code shows the `SidebarTeaserBlockRight` partial view for the teaser block (there is an identical `SidebarTeaserBlockLeft` for the left one):

```csharp
@model MyEpiserverSite.Models.Blocks.TeaserBlock
    
<div>
  <h2>@Html.PropertyFor(x => x.Heading)</h2>
  <img src="@Url.ContentUrl(Model.Image)" />
</div>
```

The following code shows the rendering view for the start page, where the blocks are displayed:

```csharp
@model MyEpiserverSite.Models.Pages.StartPage
    
<div>
  @Html.PropertyFor(m => m.MainBody)
</div>
    
<div>
  @Html.PropertyFor(m => m.GeneralContentArea, new { Tag = EPiServer.Framework.Web.RenderingTags.Sidebar })
</div>
```

### How to use tagging in a template selection:

- If you set the `AvailableWithoutTag` attribute for a template to true, it applies the template regardless of whether the rendering context has a corresponding tag.
- If the `AvailableWithoutTag` is set to **false**, or does not exist, the template is not applied unless the rendering context has a corresponding tag.

In this scenario, the following happens:

- The start page `GeneralContentArea` has the `RenderingTags.Sidebar` tag; it applies to only templates with this tag, without the `AvailableWithoutTag`, or where this is set to `false`.
- The `SidebarTeaserLeft` template has a matching tag and `AvailableWithoutTag` set, and is applied. This is also valid for the template used for the Standard block.
- The `SidebarTeaserRight` template has a matching tag, and `AvailableWithoutTag = true`. It applies the template even if you remove the `RenderingTags.Sidebar` tag from the content area.
