Community Site Documentation

Primers

Concept-oriented overviews of how the major areas of this codebase hang together.

Primers

Backend primer

The C# side of the site lives across five projects, with src/UmbracoCommunity.Web/ as the heart of it all — controllers, view model builders, services, view components, and the small ASP.NET integrations Umbraco doesn't ship by default. The aim of this primer is to give you the lay of the land on the backend, as sibling to the frontend primer. Each section sketches what's there and signposts a deeper doc for when you need it.

  • primer
  • backend
  • architecture
  • controllers
  • view-models
Primers

Backoffice extensions primer

The frontend primer covers the public site and deliberately punts on the backoffice — the Umbraco admin UI at /umbraco. This repo ships three separate backoffice extension codebases, and this primer threads them together: how an extension reaches the backoffice at all (the App_Plugins manifest), how individual pieces of UI register themselves, how those pieces talk to the secured C# APIs behind them, and why each one is its own little Vite project rather than part of the public-site build.

  • primer
  • backoffice
  • extensions
  • app-plugins
  • vite
Primers

Caching primer

Caching in this codebase isn't one system — it's a dozen small decisions made in different places, each reaching for whatever cache fit that problem. That's fine, but it means "how is X cached?" has no single answer until you know which kind of thing X is. This primer maps the caches to the problems they solve, so you can tell which one to reach for (and what invalidates it) without spelunking.

  • primer
  • caching
  • performance
  • architecture
Primers

Content modelling primer

Document types, element types, block types, and the compositions (ICompositionPageConfiguration, ICompositionSeo, ...) that share fields across them. The auto-generated PublishedModels namespace that turns all of those into typed C# classes. The view-model-builder pipeline that converts IPublishedContent into the view-shaped models the Razor templates actually render against. This primer will thread the Models/ folder structure together with the BUILDING_PAGES.md and BUILDING_BLOCKS.md how-to guides so a new contributor can hold the whole shape in their head.

  • primer
  • content-modelling
  • document-types
  • models-builder
Primers

Frontend primer

The public site's frontend lives in src/UmbracoCommunity.StaticAssets/ — a Vite + TypeScript + Lit project whose job is to build the JS, CSS, and other static assets that the Razor views in UmbracoCommunity.Web.UI load at request time. The aim of this primer is to give you the lay of the land — each section sketches what's there and signposts a tutorial, how-to, or source file for when you want to dig in.

  • primer
  • frontend
  • architecture
  • vite
  • lit
Primers

Third-party integrations primer

The external dependencies this site talks to are, almost entirely, a handful of server-side data feeds that pull content from someone else's API — and the good news is they're all the same shape, so learn one and you can read or add any of them. (There are also a couple of avatar / build-time odds and ends, covered at the end.)

  • primer
  • integrations
  • sessionize
  • third-party
  • http-client
Primers

Multi-tenancy primer

This site runs several distinct sites from one Umbraco instance — each site is a tenant, with its own root content node, its own domain, its own settings and content, but a shared media library, shared user accounts, and a single deployment. That arrangement is cheap and convenient, and it comes with exactly one rule you have to keep in your head for nearly everything you write. This primer gives you the five-minute orientation; the tutorial suite it links to has the depth.

  • primer
  • multi-tenancy
  • architecture
  • content-resolution
Primers

SEO and structured data primer

Schema.NET, the schema builders (ArticleSchemaBuilder, OrganizationSchemaBuilder, BreadcrumbSchemaBuilder), the MetaTags ViewComponent, OpenGraph and Twitter Cards in Layout.cshtml, sitemap generation, and canonical URL handling. This primer will be the orientation layer that holds the whole SEO surface in one place; the tenant-aware schema fallback tutorial is the deep dive for one specific corner of it.

  • primer
  • seo
  • schema-org
  • meta-tags

Tutorials

Short, standalone tutorials covering specific problems we've hit on the Umbraco Community site and the approaches we took to solve them.

Foundations

Secured backoffice Management API endpoints

Community content on the new Umbraco backoffice almost exclusively covers the UI side — property editors, dashboards, workspace views. The C# endpoints those UIs actually call are consistently under-documented. The BlockRestrictionApiController in this repo is a clean example of the secured backend half: routing under /umbraco/.../api/v1, locking endpoints down with [Authorize(Policy = AuthorizationPolicies.SectionAccessContent)], Swagger doc registration so the endpoints show up in the API docs, and a typed fetch wrapper on the client that pulls the user's backoffice bearer token automatically.

  • backoffice
  • api
  • authorization
  • swagger
Foundations

Configuration that inherits down the content tree

A common need in a CMS: attach a piece of configuration to something high up, and have everything below it pick the value up automatically unless it's overridden closer to home. Block Restrictions does it with allowed-block rules — set a rule once, and every page beneath inherits it — but the shape is general. This tutorial is the resolution engine behind that: how to attach a rule, resolve it for any node by walking up the tree, cache the answer so you're not re-walking on every request, and fail open when nothing is configured. It's the "how rules resolve" half of the Block Restrictions trio; the editor-wrapping refinement is what consumes the answer.

  • content-tree
  • configuration
  • inheritance
  • caching
Foundations

Building a touch-friendly drag-to-scroll slider in vanilla web components

Carousel libraries like Swiper and Embla are great until you measure the bundle cost. This tutorial will walk through the <dc-slider> component in this repo — a small Lit web component that supports touch drag (follows the finger and snaps to the nearest slide on release), desktop hover-zone navigation, and explicit arrow buttons as an opt-in. The aim is to show what a usable scroller looks like when it's built from scratch with native APIs rather than pulled in as a dependency.

  • web-components
  • slider
  • touch
  • lit
Foundations

Building an inline SVG TagHelper for Umbraco

SVGs are wonderful for icons and brand marks — small, sharp, infinitely scalable — but only if you can actually style them from CSS, and that's where things tend to get fiddly. This tutorial walks through the <svg-src> TagHelper that the Umbraco Community site uses to inline SVG files from Umbraco media straight into the rendered Razor view, where CSS and JavaScript can then reach inside them. It's a foundation piece — most of the SVG-related tutorials in this suite build on top of it.

  • svg
  • tag-helper
  • razor
  • media
Foundations

Polite animation with IntersectionObserver, requestAnimationFrame, and reduced-motion

The <dc-image-slider> component auto-scrolls a row of images — but you don't want it to keep burning a CPU core when the user can't see it, or when they've asked the OS to dial down motion. This tutorial will walk through the small composition that makes the animation cheap and polite: requestAnimationFrame for the loop itself, IntersectionObserver to pause when the slider scrolls off-screen, visibilitychange for tab switches, and prefers-reduced-motion for accessibility. The general lesson is how to animate something well without reaching for a library.

  • animation
  • intersection-observer
  • performance
  • accessibility
Foundations

Resolving content in a multi-tenant Umbraco site

Running multiple sites from a single Umbraco instance is one of those things that looks easy until you've shipped one — and then suddenly every content lookup needs to know which tenant it belongs to. This tutorial walks through the pattern the Umbraco Community site uses for multi-tenancy (several distinct sites out of one Umbraco instance) and the small set of helpers that keep every content lookup scoped to this request's tenant rather than wandering off into another one's tree.

  • multi-tenant
  • content-resolution
  • site-settings
  • ipublishedcontent
Foundations

Progressive enhancement of async-rendered DOM with MutationObserver

Progressive enhancement has a tidy mental model: the server sends working HTML, and your JavaScript layers extra behaviour on top once it loads. That model quietly assumes the HTML you want to enhance is there when your code runs. But what happens when it isn't yet — when the very thing you mean to enhance is rendered, asynchronously, by some other component that owns its own timing? Your enhancement runs, finds nothing to do, and gives up. This tutorial walks through the small MutationObserver pattern the Umbraco Community site uses to solve exactly that: the <dc-form-steps> element waits for an Umbraco Forms form to finish rendering, turns it into a multi-step form once it appears, and then disconnects cleanly.

  • progressive-enhancement
  • mutation-observer
  • forms
  • web-components
Foundations

A nonce-based Content Security Policy in ASP.NET Core Razor

A strict Content Security Policy that bans inline scripts will catch most XSS vectors at the browser level — and it will also make life impossible for every legitimate inline script your views happen to emit. The standard answer is to nonce every inline script and stamp the same nonce into the CSP header, so the browser allows scripts whose nonce matches and blocks everything else. This tutorial will walk through the NonceTagHelper + Joonasw integration this site uses, plus a per-request escape hatch for the rare endpoint that needs CSP disabled entirely. CSP-in-.NET is poorly documented; the aim is for this to be the post one of us wishes had existed.

  • csp
  • security
  • razor
  • nonce
  • tag-helper
Foundations

One master block data type, restricted per consumer

This is the content-modelling decision that the whole Block Restrictions package exists to serve, written down on its own because it's the why behind two other tutorials and it's easy to lose under their mechanics. The short version: keep a single Block Grid (or Block List) data type that holds every block the site could ever use, point every document type at that one data type, and then narrow the offered set per consumer — per document type, and by inheritance the content nodes beneath it — with a restriction rule, rather than building a separate data type for every document type that wants a different subset of blocks.

  • content-modelling
  • block-editor
  • data-types
  • architecture
Foundations

Generating utility classes from design tokens with a custom PostCSS mixin

Utility-first CSS frameworks like Tailwind give you the ergonomics of .pt-md and .mx-xs — but they want to own your design system. The rhythm mixin in this repo flips that arrangement: you write your spacing tokens in root.css as CSS custom properties, hand them to a small postcss-mixins rule, and you get the same utility classes generated for you — driven entirely by your own tokens, with no opinion from a framework. This tutorial will walk through writing the mixin, the modifier suffixes it emits, and how to layer it on an existing PostCSS pipeline.

  • css
  • postcss
  • design-tokens
  • utility-classes
Foundations

Site search backed by Umbraco's Examine ExternalIndex

"How do I add search to my Umbraco site?" is a perennial community question, and most answers stop at the single-tenant happy path. This tutorial will walk through the recently-added search on the Umbraco Community site: a SearchPage doc type, a typed SearchService that queries Umbraco's Examine ExternalIndex, a render controller that paginates the results, and the multi-tenant twist — only return hits under the current tenant's content root, so a search on Site A doesn't leak Site B results.

  • search
  • examine
  • multi-tenant
  • lucene
Foundations

Wiring Vite's manifest into Umbraco's Razor pipeline

Vite bundles your frontend and Umbraco renders your views, and sitting between the two is a single <script> tag that needs to behave completely differently depending on where it's running. Locally, you want the browser talking to the Vite dev server so you get hot module replacement (HMR — your edits to JS and CSS show up in the browser without a full page reload). In production, you want it loading a content-hashed file whose name changes with every build. Same tag, two different use cases. This tutorial walks through the pair of TagHelpers the Umbraco Community site uses to bridge them — <script vite-src> and <link vite-href> — so that you can write one stable line of Razor and let the C# work out, per environment, whether to point at localhost:5123 for HMR or at the hashed asset named in Vite's manifest.json.

  • vite
  • manifest
  • razor
  • tag-helper
  • hmr
Refinements

Caching the scoped SVG output

Once the SVG TagHelper produces deterministic scoped output, every render of the same media item is byte-identical — which is a rather lovely property to have, because it makes the whole pipeline cacheable. We can do the read + sanitise + parse + selector-prefix work once and then happily reuse the result for an hour. On cloud-hosted media in particular this is the difference between every page render making N media-storage round-trips and making zero of them, which is a meaningful difference in latency for the end user.

  • svg
  • caching
  • runtime-cache
  • performance
Refinements

Output cache policies for slow upstream APIs

When your site's performance is held hostage by a slow or rate-limited third-party API (Sessionize, GitHub, anything off-prem), aggressive caching is usually the answer — and ASP.NET Core's [OutputCache] with named policies is the right tool for it. This refinement will walk through the OutputCachePolicies class in this repo, when [OutputCache] beats ResponseCaching (it survives across instances, you control the key, you can vary by query), what cache-key shapes make sense for tenant-scoped data, and how to fail gracefully when the upstream is rate-limited or 500s.

  • caching
  • output-cache
  • sessionize
  • performance
Refinements

Per-tenant 404 pages with a custom INotFoundPageResolver

Multi-tenant Umbraco sites need multi-tenant 404 pages — it stands to reason that if a request lands on Tenant A's domain and ends up at a missing URL, the user shouldn't suddenly be looking at Tenant B's header and footer just because we couldn't find the page they asked for. This tutorial walks through the small resolver that makes that work, and the subtlety that forces it to resolve the tenant from the domain binding rather than from the (non-existent) current page.

  • multi-tenant
  • 404
  • not-found-resolver
  • routing
Refinements

Scoping inline SVG <style> to prevent class-name bleed

There's a surprisingly satisfying class of "the logo went the wrong colour" bugs that all trace back to the same root cause: inline <style> blocks inside SVGs aren't actually scoped to the SVG they live in, even though every bit of your experience tells you they ought to be. This tutorial walks through how we ran into the problem on the Umbraco Community site, why the obvious fixes don't quite hold up, and how we ended up solving it with a small change to a single TagHelper.

  • svg
  • css-scoping
  • style-isolation
  • tag-helper
Refinements

Syncing custom backoffice configuration across environments

You've built a feature whose configuration lives in your own table — for us, block-restriction rules keyed by document type. It works beautifully on your machine. Then someone asks the obvious question: a rule you set up locally needs to be on staging and production too, and ideally it should be reviewable in a pull request like the rest of the codebase. Suddenly "it's in the database" is a problem, not a feature. This tutorial is how Block Restrictions makes its configuration travel between environments and live in git.

  • umbraco-cloud
  • deployment
  • configuration
  • version-control
  • ef-core
Refinements

Tenant-aware fallback for schema and SEO metadata

Structured data — the small blocks of Schema.org JSON-LD that crawlers like Google and Bing look for in your page head — needs a publisher: an Organization with a name, a URL, and a logo, that they can attribute the content to. On a multi-tenant Umbraco site, that publisher is naturally per tenant — Site A's publisher is the Umbraco Community brand, Site B's is the events microsite, and so on. The challenge is that tenant brand metadata is editor-configurable, which means (let us be honest with each other here) that it's also editor-forgettable. This tutorial walks through the small pattern that produces valid Organization schema whether the tenant's brand fields are filled in, partially filled in, or entirely absent.

  • seo
  • schema-org
  • multi-tenant
  • social-settings
Refinements

Wrapping Umbraco's native block editor with restriction filtering

The Block List and Block Grid editors are two of the most useful things Umbraco ships, and by default they offer every block a data type is configured with to every editor, on every node. We wanted to narrow that list per document type (with inheritance down the content tree) — a "Blog Post" should only offer a handful of blocks, a "Landing Page" the full set. This tutorial is about the backoffice side of that: how to enforce the restriction in the editing UI without reimplementing the block editor, by wrapping the native one instead of replacing it. The sting in the tail is copy-and-paste, which breaks in a genuinely puzzling way once you wrap, and needs a little boilerplate to put right.

  • backoffice
  • property-editor
  • block-editor
  • clipboard