Template Hierarchies
Outline
- Templates are MVC renderers registered for content (controller and 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 and property rendering, especially via
@Html.PropertyFor
Overview
This article gives you a mental model for how Optimizely CMS 12 (ASP.NET Core) resolves which controller and 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 - 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 one. 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 where the wrong one is winning due to tags or fallbacks. Those two causes cover the vast majority of template resolution issues.
Common Rendering Contexts
Select a context to expand and read the details.
Full page request ▼
A URL routes to a page and the runtime renders it as a complete response. The page controller's Index action is invoked and returns the full page view.
Partial rendering ▼
A page renders blocks inside a ContentArea or another container. Each block is resolved independently through its own DisplayTemplate or ViewComponent.
Display channel variation ▼
Different templates may be chosen for different device or channel scenarios when display channels are configured. The same content type can render completely differently for web vs. mobile vs. email delivery.
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. This avoids property duplication across content types.
Convention to adopt: Put shared page properties in one base type, then keep specialized page types small and focused. Resist the urge to add one-off properties to the base.
What to Put in a Base Page Type
Select a category to expand and read the details.
Shared heading and title conventions ▼
Page naming and heading behavior should be consistent across the site. A shared Heading property on the base type ensures every page type follows the same convention without repetition.
SEO fields ▼
Common meta title and meta description fields should live on the base type so every page type automatically exposes them to editors - no manual addition required for each derived type.
Layout settings ▼
Theme flags, navigation options, and layout switches that should be available site-wide belong on the base type. This prevents derived page types from needing to redeclare structural settings.
Shared ownership and classification fields ▼
Fields like content owner, category, or taxonomy classification that are used across many page types should be defined once in the base rather than duplicated - especially important when these fields drive search indexing or personalization.
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.
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. Build view models in the controller action, not in the view.
Views Should Be Easy to Find
Follow standard MVC placement conventions so view discovery stays predictable. Avoid project-specific folder structures that force full-text search as the primary navigation strategy.
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
Select to expand and see the recommended structure.
Recommended folder structure ▼
-
/Controllersfor page controllers -
/Viewsfor page views -
/Views/Sharedfor shared layouts and partials -
/Views/Shared/DisplayTemplatesfor block and property templates
Use any convention your team prefers, but keep it consistent and document it - a short README in the solution beats tribal knowledge.
DisplayTemplates Conventions
When you render properties like ContentArea using @Html.PropertyFor(...), Optimizely resolves block and property rendering via DisplayTemplates.
Two conventions to follow consistently:
- Store block and property display templates under
~/Views/Shared/DisplayTemplates. - Name the template file after the type it renders - for example,
ContactBlock.cshtml.
- Is the block type rendered via a DisplayTemplate with the expected filename and location?
- Is a different template winning due to a tag or fallback?
- 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.
Tag Usage Guidance
Select a topic to expand and read the details.
When tags are worth it ▼
- Teaser vs. full view: A compact card in a listing vs. the complete standalone page view of the same content type.
- Placement-based rendering: The same block rendered differently in a header vs. the main content area.
- Channel-based rendering: A different template selected for a specific configured display channel.
A safe minimum approach ▼
- Start with one template per content type. Keep it simple until you have a clear scenario that requires a second rendering option.
- Add a second template only when the scenario has a name. "Teaser" is a scenario. "Different" is not.
- Keep tag names simple and consistent. Define a set of allowed tags and document it - undocumented tag proliferation becomes a debugging hazard.
Developer Checklist
Use this ABCD map to keep rendering predictable across a growing project.
- Align models and templates. Use inheritance for shared properties. Avoid copy/paste across page types.
- Be consistent with MVC structure. Controllers and views should be locatable using standard MVC conventions without needing to search the codebase.
-
Control partial rendering intentionally. Use
Views/Shared/DisplayTemplatesfor block and property templates. Add tags only when you have a clearly named scenario that requires a second variant. - Document your conventions. A short README in your solution beats tribal knowledge every time.
Quick Practice (Self-Check)
Practice Scenarios
Select a scenario to expand and see the answer.
ContentArea rendering debug: a block layout looks wrong ▼
You render a ContentArea with @Html.PropertyFor(m => m.MainContentArea). A block layout looks wrong. Where do you look first?
- Check DisplayTemplates - correct filename and location under
Views/Shared/DisplayTemplates? - Check whether a tag-based template is winning unexpectedly.
- Confirm you are in the context you think you are (full page vs. partial rendering inside a container).
Shared metadata fields across two page types ▼
Your ArticlePage and LandingPage share metadata fields. What should you refactor?
- Create or expand a base page type and move the shared properties there.
- Consider a base controller if shared rendering behavior is also duplicated across the two page controllers.
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 and 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.
