Foundations
Pieces of code that other tutorials build on. Read these first if a refinement says it's a prerequisite — each one is a self-contained deep dive on a single technique, reusable outside the specific problem that motivated it.
-
How to secure a custom backoffice Management API endpoint
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. This tutorial is the other half: how BlockRestrictionApiController — and its three near-identical siblings elsewhere in this repo — routes under the backoffice, locks itself down to users with the right section access, registers itself in the API docs, and gets called from a typed client. It's a foundation piece, and a direct sequel to the backoffice extensions primer, which sketches the client side in a few paragraphs and explicitly defers the backend half to here.
-
How to make configuration cascade down a content tree, with document type overrides
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.
-
Building a touch-friendly drag-to-scroll slider in vanilla web components
Carousel libraries like Swiper and Embla are excellent, and most of what they give you is overkill for a block that shows a handful of cards or slides and needs prev/next navigation. This tutorial walks through <dc-slider> — a small custom element in this repo that handles touch drag (following the finger, snapping to the nearest slide on release), desktop hover-zone navigation, and an explicit-arrow-button opt-in, all without a dependency. It's a foundation piece, reused as-is by two different content blocks with zero component-side changes between them.
-
GitHub OAuth login for Umbraco members
Community members on this site don't have passwords. There's no "forgot password" flow to build, no email-verification loop to maintain, and no credential store to worry about leaking — sign-in is GitHub or nothing. For a site built for developers, most of whom already have a GitHub account they use daily, that's not a compromise; it's the obvious identity provider. This tutorial walks through how that's wired into Umbraco's member system, and the handful of sharp edges that bit along the way. It's a foundation piece — nothing else in this suite builds on it yet, but it's the base the account page and the wider community-profile feature sit on top of.
-
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.
-
How to pause an animation when it's off-screen, the tab is hidden, or the user prefers less 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.
-
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.
-
Turning a form that renders asynchronously into a multi-step flow
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.
-
How to keep inline scripts and styles working under a strict Content Security Policy
A strict Content Security Policy that bans inline scripts catches most XSS vectors at the browser level — the browser simply refuses to run a <script> tag that isn't on an approved list. The trouble is that a real site has inline scripts and styles: build-tool bootstrap snippets, per-instance background colours an editor picked in the backoffice, structured-data JSON-LD. Ban all of them and half the site breaks; allow 'unsafe-inline' and you've defeated the point of having a CSP at all. The standard middle ground is a nonce — a random token generated once per request, stamped onto both the CSP header and every inline tag you actually trust, so the browser runs only the ones whose token matches. This is a foundation piece: nothing else in this suite builds on it, but it underpins every inline <script> and <style> in the codebase.
-
How to create and use a master block list that you can filter per tenant
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.
-
How to generate spacing utility classes from your own design tokens
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.
-
How to add multi-tenant site search using Umbraco's built-in Examine index
"How do I add search to my Umbraco site?" is a perennial community question, and most answers stop at the single-tenant happy path: query ExternalIndex, render the hits, done. This tutorial walks through what that answer actually looks like once you add a second tenant, a second content source, and a page full of results that need to paginate cleanly — the site search on the Umbraco Community site, built entirely on Umbraco's own zero-config Examine index. It's a foundation piece: nothing else in this suite builds on it, but it's a self-contained answer to a question that comes up on every multi-page Umbraco site sooner or later.
-
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.