Skip to main content

Outline

At a glance
  • Templates are MVC renderers registered for content (controller + view, or another renderer).
  • Hierarchy is how the runtime chooses among multiple templates, not the content tree.
  • The usual decision points are context (full page vs. partial), type match, then tags/channels.
  • Inheritance keeps content models and controllers reusable and consistent.
  • DisplayTemplates are the workhorse for block/property rendering (especially via @Html.PropertyFor).

Overview

This lesson sits under Rendering Content Templates. It gives you a mental model for how Optimizely CMS 12 (ASP.NET Core) resolves which controller/view renders a content item, and the conventions that make the setup predictable for the next developer (including Future You).

Template selection hierarchy

You do not need to memorize an internal algorithm. Use this order to predict outcomes and debug quickly.

  1. Start with the rendering context

    Ask: are we rendering a full page request, or rendering content inside another view (for example, inside a ContentArea)? Context narrows candidate templates immediately.

  2. Match by content type

    Optimizely looks for templates that can render the content type being requested. If your models use inheritance, you can reuse patterns without duplication.

  3. Refine with tags and TemplateDescriptor metadata

    If more than one template can render the same type, tags let you steer which one wins in a given scenario (for example, full page vs. teaser, or different placements).

  4. Fall back to general templates

    If a specialized template is missing, the system can fall back to a more general template. Your goal is to make fallbacks intentional and consistent, not accidental.

Debug tip

If something renders unexpectedly, assume you have either (1) a different context than you think, or (2) multiple templates and the wrong one is winning due to tags or fallbacks.

Common rendering contexts you’ll actually see
  1. Full page request

    A URL routes to a page and the runtime renders it as a complete response.

  2. Partial rendering

    A page renders blocks inside a ContentArea or another container.

  3. Display channel variation

    Different templates may be chosen for different device/channel scenarios (when configured).

Content model inheritance conventions

A common approach is a base page type for shared fields (metadata, SEO, layout settings), then derived page types for specific page behavior.

public abstract class SitePageData : PageData { public virtual string Heading { get; set; } } public class ArticlePage : SitePageData { public virtual XhtmlString MainBody { get; set; } }
Convention to adopt

Put shared page properties in one base type. Then keep specialized page types small and focused.

What to put in a base page type
  1. Shared heading/title conventions

    Make page naming and heading behavior consistent across the site.

  2. SEO fields

    Common meta title and meta description fields.

  3. Layout settings

    Theme flags, navigation options, and layout switches that should apply site-wide.

  4. Shared ownership/classification fields

    Fields like content owner, category, or classification used across many page types.

MVC conventions developers should follow

Optimizely CMS 12 builds on standard ASP.NET Core MVC. Treat templates like normal MVC: controllers return views, views use view models, and shared UI moves into partial views.

  1. Page controller pattern

    Use one page controller per page type when it improves clarity. Use a base controller when many page types share logic (for example, building common layout data).

public class ArticlePageController : PageController<ArticlePage> { public IActionResult Index(ArticlePage currentPage) { var vm = new ArticlePageViewModel(currentPage); return View(vm); } }
  1. Views should be easy to find

    Follow standard MVC placement conventions so view discovery stays predictable. Avoid project-specific folders that force full-text search as the primary navigation strategy.

  2. Use partial views for reusable UI

    Navigation, cards/teasers, and small components should typically be partial views. This reduces duplication and keeps UI consistent across templates.

A simple folder convention (example)
  • /Controllers for page controllers
  • /Views for page views
  • /Views/Shared for shared layouts and partials
  • /Views/Shared/DisplayTemplates for block/property templates

Use any convention your team prefers, but keep it consistent and documented.

DisplayTemplates conventions

When you render properties like ContentArea using @Html.PropertyFor(...), Optimizely commonly resolves block/property rendering via DisplayTemplates.

@Html.PropertyFor(x => x.MainContentArea)

Common convention for DisplayTemplates:

  1. Store block/property display templates under ~/Views/Shared/DisplayTemplates.
  2. Name the template file after the type it renders (for example, ContactBlock.cshtml).
Fast troubleshooting checklist
  1. Is the block type rendered via a DisplayTemplate with the expected filename and location?
  2. Is a different template winning due to a tag or fallback?
  3. Is the block being rendered in a different context than you expected?

TemplateDescriptor and tags conventions

Use tags (and TemplateDescriptor metadata) only when you truly need multiple templates for the same content type. This is common for full page vs. teaser rendering, or a block that needs different layouts in different placements.

When tags are worth it
  1. Teaser vs. full view

    A compact card in a listing vs. the full page.

  2. Placement-based rendering

    Same block rendered differently in header vs. main content.

  3. Channel-based rendering

    A different template for specific channels, when configured.

A safe minimum approach
  1. Start with one template per content type

    Keep it simple until you have a clear scenario that requires a second option.

  2. Add a second template only when the scenario has a name

    For example: “Teaser” is a scenario. “Different” is not.

  3. Keep tag names simple

    Use consistent naming and document your tag set.

Developer checklist

Use this as your ABCD map to keep rendering predictable.

  1. Align models and templates

    Use inheritance for shared properties. Avoid copy/paste across page types.

  2. Be consistent with MVC structure

    Controllers and views should be easy to locate using standard MVC conventions.

  3. Control partial rendering intentionally

    Use Views/Shared/DisplayTemplates for block/property templates. Add tags only when you truly need multiple rendering variants.

  4. Document your conventions

    A short README in your solution beats tribal knowledge.

Quick practice (self-check)

ContentArea rendering debug

You render a ContentArea with @Html.PropertyFor(m => m.MainContentArea). A block layout looks wrong. Where do you look first?

  1. Check DisplayTemplates (filename and location).
  2. Check whether a tag-based template is winning unexpectedly.
  3. Confirm you are in the context you think you are (full vs. partial).
Shared metadata fields

Your ArticlePage and LandingPage share metadata fields. What should you refactor?

  1. Create (or expand) a base page type for shared properties.
  2. Consider a base controller if shared behavior is also duplicated.

Conclusion

Template hierarchies in CMS 12 are about predictable selection: understand context, keep type matching straightforward, use inheritance for reuse, rely on DisplayTemplates for block/property rendering, and introduce tags only when you have a clearly named scenario. If your structure is consistent, debugging becomes quick and boring, which is the best kind of debugging.