hear what the community is talking about

Community Blogs

We’ve gathered blog posts from across the internet to highlight the many voices that make up our community. Powered by Umbraco, this space brings together diverse stories, ideas, and perspectives in one easy‑to‑explore hub. Dive in and discover what the community is creating, sharing, and talking about.

Want to add your future blog posts to the list? Submit it here!

Umbraco

Umbraco Upgrade Decisions: Start With Knowing Where You Stand

Choosing whether to stay on, upgrade or rewrite an Umbraco site starts with an honest check of where it stands now: the version and runtime, known vulnerabilities, package dependencies, theme coupling and technical debt. It also considers wider pressures such as performance, accessibility, usability and changing traffic patterns, so the decision is based on evidence rather than assumptions. Continue reading

by Rachel Breeze

Multi-Site Umbraco without the shared-tree mess

At some point in a multi-site Umbraco project, someone creates a "Global" or "Shared" content node to hold whatever multiple sites need in common, and it never stops growing. Six months later it's holding navigation overrides, campaign banners some sites don't use, and properties nobody remembers the purpose of. This isn't really a technical failure, it's what happens when a single-site content model gets stretched across multiple sites without anyone deciding what "shared" actually means. Getting the structure right from the start, across content, media, permissions and search, keeps the tree something editors can still read a year later.

by Dave Jonker

Introducing Umbraco.Community.PropertyNavigator

Umbraco.Community.PropertyNavigator brings back the v13 Content field dropdown for Umbraco 17 — click the Content view to search and jump straight to any property on a node.

by Nathaniel Nunes

Most Automation Is a Decision, Not a Conversation

Most of the AI steps I see people build into automations aren’t really about generating anything. They’re about deciding something. Is this comment...

by Matt Brailsford

Smidge 5 is out

Smidge 5 is out Smidge 5.0.0 is out on NuGet! It's been about a year since the last release, so sorry 'bout that 😅. This one turned into a proper major version rather than another collection of small fixes: Smidge is now on .NET 10, NUglify is the only minifier, the MVC dependency is gone, the package structure is simpler, and a few long-running production bugs have finally been tracked down properly. The full release notes have every detail. This is the story behind the parts that matter most. One minifier instead of three Smidge has historically shipped with its own hand-rolled JavaScript and CSS minifiers, JsMinifier and CssMinifier, while also offering NUglify through the separate Smidge.Nuglify package. That made sense when those minifiers were first added, but over time it meant maintaining two different approaches to the same job. The built-in implementations had their own edge cases and bugs, while NUglify was already the better maintained and more capable option. In v5 the old JSMin and CSSMin implementations are gone. NUglify is now included directly in the main Smidge package and is used automatically by the normal AddSmidge() and UseSmidge() setup. The separate Smidge.Nuglify package, along with AddSmidgeNuglify() and UseSmidgeNuglify(), is no longer needed. This is a breaking change, but it's also a much simpler default. There is one minification pipeline, one set of behaviour to test, and source maps work without having to opt into another package. .NET 10 and no more MVC dependency Smidge now targets .NET 10. The old MVC controllers used to serve bundle, composite-file and source-map requests. Those have been replaced with lightweight minimal API endpoints, with the old action-filter behaviour moved into endpoint filters. That removes the MVC dependency entirely. If you're using Smidge in a small ASP.NET Core app you no longer need to pull MVC into the application just to serve a CSS or JavaScript bundle. As part of that work, the old Smidge.Core project has also been folded into Smidge. There isn't much value in making consumers understand the internal package split, and having the public surface spread across assemblies made the dependency graph harder to follow than it needed to be. The practical result is fewer packages, fewer setup methods and less framework machinery underneath it all. The random bundle crash was actually a race condition There have been a couple of reports over the years of Smidge suddenly throwing a NullReferenceException from BundleFileSetGenerator.ValidateFile. The frustrating part was that it only seemed to happen in production, often after an application had been running for a while, and recycling the app pool would make it disappear. The immediate symptom was a null file entry inside a bundle, but there was no obvious path that intentionally added one. I added defensive checks so a bad entry could be logged and skipped instead of taking down the entire bundle response, but that still didn't explain how it got there. The missing clue came from somebody who found that wrapping their view-based bundle registration in an application-level lock made the problem go away. That pointed at the real problem. BundleManager is a singleton and its bundle dictionary was thread-safe, but each bundle stored its files in a normal List<IWebFile>. View-based declarations can run from multiple concurrent requests, which meant multiple threads could call List<T>.Add on the same list at the same time. List<T> doesn't support concurrent writers, and if two additions collided during an internal resize the list could lose entries or end up with null slots. It wasn't the cache buster, app pool idle state or a missing source file. It was memory corruption inside a concurrently mutated list. Bundle.Files is now backed by an immutable list and updated with ImmutableInterlocked.Update. Reads get a complete, unchanging snapshot with no locking cost, and concurrent writers use a compare-and-swap loop rather than corrupting the same backing array. There are also defensive null checks at bundle creation and file-set generation, because a bad input still shouldn't turn into a broken page. A concurrency test that registers files from 50 tasks at once reliably lost entries against the old implementation. With the new implementation every file is present and there are no nulls. This is probably the most important fix in the release even if it's not the flashiest one. Missing cache files now return 404 The other production hardening work is around stale or invalid bundle URLs. Composite bundle URLs contain references to already-processed cache files. If the cache was cleared, an in-memory cache disappeared after an application restart, or somebody deliberately requested a made-up file hash, Smidge used to call a throwing file lookup. That surfaced as a 500 and could be triggered repeatedly, which made it a fairly easy denial-of-service path. The HTTP-facing composite and source-map handlers now use non-throwing lookups. If a requested cache file isn't there, Smidge returns a normal 404 instead of throwing. Internal invariants still throw where they should - a source file configured in a real bundle disappearing is different from an arbitrary client asking for a stale cache path. The same work fixed an older source-map issue where an unexpected sourceMappingURL request could fail with Could not parse ... as a valid smidge path. Invalid or missing source-map requests now just return 404, which is what the browser expects anyway. Globs and directory bundles There are a couple of smaller file-matching fixes that are worth calling out. Pointing a bundle at a bare directory used to expand it to *.*. That could pull generated files such as .js.gz or .map into a JavaScript bundle and then hand binary gzip data to NUglify, which ended about as well as you'd expect. Bare directory bundles are now constrained to the bundle type, so JavaScript bundles get .js files and CSS bundles get .css files. Recursive glob patterns are supported as well: bundles.CreateCss("site", "~/assets/css/**/*.css"); They already worked for the common physical-file-provider case through .NET's built-in globbing support, but the fallback used for embedded and composite file providers didn't understand the **.css form correctly. Both **.css and **/*.css now behave consistently across providers. Breaking changes This is a major version, so there are a few things to account for when upgrading: Smidge now targets .NET 10. Smidge.Core has been merged into Smidge and the separate assembly no longer exists. Smidge.Nuglify, AddSmidgeNuglify() and UseSmidgeNuglify() are no longer needed. NUglify is built into the normal Smidge setup. The old built-in JsMinifier and CssMinifier are gone. A few old MVC handler and filter types are now internal implementation details. Bundle.Files is now IReadOnlyList<IWebFile> rather than a mutable List<IWebFile>. Use Bundle.AddFile(IWebFile) if you need to add files programmatically. For most applications the upgrade should mainly be removing the old NUglify-specific setup and package reference, then rebuilding against .NET 10. If you only used the standard AddSmidge() / UseSmidge() APIs and predefined bundles, there shouldn't be much else to change. Build and release cleanup The dependency set has been brought up to date, including the Microsoft.Extensions packages, SourceLink, xUnit, the test SDK and Moq. I've also replaced GitVersion with MinVer. The version now comes directly from the Git tag: normal builds produce a 5.0.0-ci.<height> prerelease version and the v5.0.0 tag produces exactly 5.0.0. It's considerably less machinery for what Smidge needs. Give it a go Smidge 5.0.0 is available on NuGet, and the source and full release notes are on GitHub. This release took longer than it should have, but it also ended up being more substantial than I'd originally planned. The package setup is simpler, the runtime surface is smaller, and the two worst classes of production failure - corrupted bundle registrations and attacker-reachable cache misses turning into 500s - are now properly fixed rather than worked around. If you run into anything while upgrading, please open an issue. And thanks to everyone who kept reporting the weird, hard-to-reproduce failures over the years. Those are never the fun ones to investigate, but in this case they led to a much better fix than another null check 🙂

by Shanon Thompson (Deminick)

Retiring Named Pipes: A Simpler Fix for Dev Port Conflicts

Update, 05-10-2026: The first version of this post said the port lived in git config --worktree. That turned out to have a catch: git worktree add...

by Matt Brailsford

Building a QR-Code Walking Route App with Umbraco, Next.js and AI

.I built a QR-code walking route app using Umbraco 18, Next.js and an AI coding agent. In this post I explain the content model, the headless setup, how MCP was used to configure Umbraco, and which rules helped keep the AI-generated implementation co...

by Johan Reitsma

Umbraco Upgrade Decisions: Is This Still the Right Site

Knowing where a site stands technically is only half the picture. In part two I look at whether the site is still fit for purpose, what Umbraco's support lifecycle actually means in practice, and what staying on a version honestly commits you to, before deciding whether change is even the right call. Continue reading

by Rachel Breeze

Running the Umbraco backoffice behind Windows Authentication

From Umbraco 14 onwards, the backoffice stops working on sites where IIS has Windows Authentication on and anonymous authentication off. Here's why that happens, and a small package that fixes it for Umbraco 17 and 18.

by Justin Neville

Upgrading Umbraco 13 to 17 on Umbraco Cloud? Set the .NET runtime first

Umbraco Cloud deploy failing with NETSDK1045 after a 13 to 17 upgrade? The .NET runtime setting in the Cloud portal needs changing.

by Nathaniel Nunes

Manage video in Umbraco, stream elsewhere

uMux now supports Umbraco 18, joined by uBun and uGum for syncing local videos to Mux, Bunny Stream or Gumlet without changing the editor workflow.

by Søren Kottal

How I built the Umbraco Kent Meetup website using Umbraco and Astro

How I built the Umbraco Kent Meetup site headless on Umbraco 17 and Astro, hosted on Azure for less than a Cloud starter, without writing any of the code myself.

by Justin Neville