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 deletedContentSelector— likewise, and it may point at an unpublished pageLink— 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.