Developer Guide

Templates and content zones

Rendering a type, and letting an author compose a page out of blocks.

A template is a Razor page named after the type it renders, with CmsPageModel<T> as its model.

@model CmsPageModel<StandardPage>

<h1>@Model.Content.Heading</h1>

@Cms.Html(Model.Content.Body)

@await Html.RenderZoneAsync(Model.Content.BodyBlocks)

Model.Content is your class, typed. Nothing casts, and nothing looks a field up by string.

Escaping is your decision, and the type tells you which

string and TextArea are plain text: render them normally and Razor escapes them. HtmlString is author markup and must go through Cms.Html(...), which writes it unescaped.

The type of the property is the whole signal. If you find yourself calling Html.Raw on a string field, the field is the wrong type.

Content zones

A ContentZone is not rendered field by field. It is a list of blocks an author composed, in the order they chose:

@await Html.RenderZoneAsync(Model.Content.BodyBlocks)

Each block renders through its own partial — StandardBlock through /Pages/Shared/Blocks/StandardBlock.cshtml — by the same class-name convention page types use.

The zone is the load-bearing part of a page type. It is what makes every block feature reachable at all, forms included. A page type without one can render its own fields and nothing else, which is why the Starter ships one from the start.

Handling absence

Three fields can point at nothing, and a template that assumes otherwise breaks the day an author clears one:

  • MediaReference — the target may have been deleted
  • ContentSelector — likewise, and it may point at an unpublished page
  • Link — has three shapes, and one of them is empty

Resolve, check, and have something to render when the answer is nothing.

Layouts

Pages/_ViewStart.cshtml sets the layout once, so a new content type needs a .cshtml and nothing else. Override it in a template that wants different chrome:

@{ Layout = "_BareLayout"; }

A layout has no typed model, so what the request resolved to is reached through the context: Context.CmsCulture(), Context.CmsPath(), Context.CmsCanonical(), Context.CmsAlternates() and Context.CmsRobots(). All are empty on a request that is not a rendered page, which is what makes them safe to call from a layout shared with pages the CMS never routed.