SuperAPI AI Directory

umbraco-templating

Coding & development

View repository

Updated 10-20 04:43

About this skill

Templating

Umbraco renders every content page through ASP.NET Core MVC: a request is matched to a content node, routed to a controller (Umbraco's RenderController unless you hijack it), and rendered by the node's Template, a Razor view.

Pick the building block

Need Use Reference
Markup for a Document Type's pages, shared page shell Template + master layout views-and-partials.md
Reusable markup that only formats the model it's given Partial view views-and-partials.md
Reusable widget with its own logic or injected services View component view-components.md
Page needs data that isn't on the node (external API, paging, per-request logic), or a different view per template Route hijacking: RenderController + view model route-hijacking.md
Accept a form POST from a content page Surface controller surface-controllers.md
JSON / REST endpoint Not a templating concern: plain ASP.NET Core controller Other controller types in surface-controllers.md

Before writing any code, open the reference for every block you use and start from its asset. The Umbraco 17 traps that break these pages are covered only there. For example, a hijacked Index overload throws AmbiguousMatchException, and an *Async surface action name doesn't route. Code written from this page alone will hit them.

How to decide

Use the lightest block that does the job: template → partial → view component → hijacked controller. Querying other content doesn't need a hijack: a template or view component can do it. A single page often combines several blocks: assets/productPage.cshtml is a hijacked template that invokes a view component and renders a form partial.

A new template renders only once it's registered in Umbraco and assigned to the Document Type, otherwise the page 404s: see step 3 of views-and-partials.md.

Finish every building block with a build and a request to a page that exercises it. Check Umbraco:CMS:ModelsBuilder:ModelsMode first:

  • Nothing or SourceCode*: build with dotnet build -p:RazorCompileOnBuild=true. The stock Umbraco csproj sets RazorCompileOnBuild=false, so a plain dotnet build never compiles a view and passes even when they're broken.
  • InMemoryAuto (Umbraco's default): the generated models exist only at runtime, so that build fails with CS0246 on a healthy site. A plain dotnet build plus the page request is the check. Don't change the user's ModelsMode or rewrite their views to make the Razor build pass.

If no runnable project is available, say so rather than claiming it works.

Version compatibility

Targets Umbraco 17. The assets/ compile and render on Umbraco.Cms 17.5.3. On Umbraco 18, PublishedContentWrapped takes only (IPublishedContent), so drop the fallback argument from the view model.

Several snippets on the Umbraco 17 docs pages are v8-era and do not compile or run on 17. References whose docs pages have broken snippets list them under Docs corrections. Trust the assets over the docs where they disagree.

Best practices

  • Views render, they don't fetch. No IContentService/IMediaService in a view. Move logic into a view component or a hijacked controller. For performance traps (deep traversal, repeated Root()), see the sibling skill umbraco-common-pitfalls.
  • Async rendering only: <partial>, await Html.PartialAsync, await Component.InvokeAsync. Never Html.Partial/Html.RenderPartial, which are sync-over-async and can deadlock (analyzer MVC1000).
  • Typed values. Use @(Model.Value<T>("alias")), or the generated ModelsBuilder property. The @(...) is required: in a bare @Model.Value<T>(...) Razor reads <T> as an HTML tag (CS1503). Rich text converts to IHtmlEncodedString, so render it directly. Never Html.Raw an editor string.
  • Keep the master layout generically typed (UmbracoViewPage or a shared interface, not UmbracoViewPage<Home>), or a hijacked page with a custom view model will throw when the layout binds.

Validation

Objective assertions live in evals/evals.json; run them with umbraco-skill-evaluator. Every asset is Asserted by the runtime gate (examples/render-pipeline/, blank host): the hijacked route, query binding, property fallback, the view component and both form-post paths are checked over HTTP.