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

Announcing the new ProWorks Umbraco Media Audit package!

One of the things I've been asked for by clients more often than I'd expect in the last few months is to help them audit all their media so that they can clean out everything they're not using. The good news is that with access to a database and Claude, writing a little reusable python script to scrape everything was exceptionally easy. But by the third request I'd decided maybe that rather than doing this for my clients I could give them the autonomy and flexibility to do this by themselves on demand. After all, it's a waste of their time and budget to need a developer to audit their media whenever they're wanting to clean it up. The benefit of this package is it gives your content editors the autonomy to audit and clean up their own media on their own schedule whenever they want! So with this, I present to you the new ProWorks Umbraco Media Audit package that I've released for Umbraco 17 and 18! This tidy little package adds a dashboard into your media section where you can run an audit on all of your media, discover what's in use and what isn't all in one location without having to click through item after item. There's some caveats of course - the audit is based on Umbraco's own reference tracking, so the main list only knows about media that's referenced with a picker. If a client pasted a URL manually somewhere, it can still show up as unused. The good news is that before anything is actually deleted, the Media Audit does a second scan through all your content for the item's ID, file path, or file name, and skips anything it finds. If you're linking to an item in an e-mail marketing campaign though, there's still no way for the Media Audit to tell. So it's important that human eyes look at these items when determining if they're still relevant, and getting detail on an item by item basis is very easy to do. And once an admin-only user has determined none of the media is in use, they can mark them all for deletion and move them into the recycle bin. If they're really content all is well then there's even a purge button to clear the recycle bin and remove the items from disk! Every delete and purge is also recorded in a deletion log, so you can always see who removed what, when, and how much space it freed up. Sometimes files end up in your media folder without a matching media item in Umbraco - think old migrations or files copied straight onto the server. The new Scan Disk Media button finds those orphaned files, lists their size and last modified date, and lets an admin export or delete them. Each file gets re-checked right before it's deleted, just to be safe! To get started, install it with dotnet add package ProWorks.Umbraco.MediaAudit. Version 1.x is for Umbraco 17 and version 18.x is for Umbraco 18. We have a few features that we want to add in the future: Field auditing - do you have your alt text or captions filled out? We'd like to make this "plug and play" so you can add any fields that you want and the tool will check if they're empty. Integrating the above with Umbraco AI - if a field is empty, fill it in with Umbraco AI's integrations to read media files and write alt text. Display a thumbnail of the item in the dashboard (if it's an image) to help with visual recognition Check if images don't have crops selected or set Show media that's hard-linked in rich text as "in use" in the main audit list, not just at delete time - thanks to JLD for the suggestion! Are there any features that you'd love to see? Let us know! Version history October 15, 2026 (18.1.0 / 1.1.0): Added Scan Disk Media and fixed audits getting stuck on "Running" under IIS. September 9, 2026 (18.0.2 / 1.0.3): Fixed deletion log entries being truncated on SQL Server. September 8, 2026 (18.0.1 / 1.0.2): Fixed a startup failure on SQL Server. September 2, 2026 (18.0.0): Released the Umbraco v18 version! August 28, 2026 (1.0): Launched for Umbraco 17.

by Proworks

Quick Tip: Extending the Delivery API's Content Response

A quick look at how to extend Umbraco 17’s Delivery API response with content from elsewhere in the content tree, without building a custom endpoint....

by Johan Reitsma

Notify a Teams channel when you publish content, using Umbraco Automate

Use Umbraco Automate to post a notification to a Microsoft Teams channel whenever content is published, with no custom C#. A step by step walkthrough on Umbraco 17.

by Nathaniel Nunes

Why I choose Umbraco over WordPress

Umbraco and WordPress solve different problems, and after years of building on both I know which one I want to still be maintaining in five years.

by Justin Neville

Examine v4 is out

Examine v4 is out Examine v4.0.0 has been released to NuGet! It's been in beta since October 2023, so it feels great to finally have this one out the door. The headline feature is faceting, which people have been asking me about for years, along with taxonomy indexes, deep paging, and a fair amount of modernization of the codebase. What took so long The first facet commit landed in November 2022 from Nikolaj Brask-Nielsen who did the original implementation, and then Chad Currie built taxonomy index support, deep paging and a lot of the surrounding infrastructure on top of that. Between them that's well over a hundred commits of genuinely hard Lucene work, and honestly the community contributions on this release have been fantastic. After that it sat in beta across nine releases. Part of that is simply that Examine is a side project I maintain around a full time job, and nothing was broken enough in the betas to force the issue. The other part is that Examine v4 is built on Lucene.NET 4.8, which is itself still in beta, and I didn't want to ship a final release that depended on a beta of something else. So the plan was to wait for Lucene.NET 4.8 to go stable first. Lucene.NET 4.8.0-beta00001 was released in May 2017, and the last stable Lucene.NET release was 3.0.3 back in 2012, so in hindsight that was a fairly optimistic plan 😊 That's not a criticism by the way - porting Lucene to .NET is an enormous amount of work done by volunteers and the project is very much active. beta00018 shipped in June this year and the hope is for a final 4.8 release before the end of the year. In practice these betas have been running in production on a very large number of Umbraco sites for years now, so the beta label was never really the risk that it sounds like. What changed is that Umbraco needed v4. What's new in v4 Faceting is the main one. You declare facet fields in your field definitions like any other field, request facets on a query with WithFacets(...), and read them back with GetFacet(): var results = searcher.CreateQuery("content") .Field("nodeName", "product") .WithFacets(facets => facets.FacetString("category")) .Execute(); foreach (var value in results.GetFacet("category")!) { Console.WriteLine($"{value.Label}: {value.Value}"); } There are facet types for full text, numerics, dates and ranges, and each has a taxonomy variant. Being able to do this natively in Examine, rather than dropping down to Lucene yourself, is the bit I'm most pleased about in this release. The rest of it: Taxonomy indexes - a sidecar index enabled with UseTaxonomyIndex, which gives you hierarchical facets and faster faceting generally. This started out as a separate index type and ended up merged into LuceneIndex, which is a much better place for it. Deep paging - SearchAfterOptions lets you page a long way into a result set without Lucene having to materialize everything up to your offset. It works with faceted queries too. Boost factors and phrase queries - WithBoost() on individual search values, and a proper Phrase() instead of the old escaped value approach. Nullable reference types throughout, with warnings as errors, so every public API is annotated. You'll likely get some new warnings when you upgrade, all of which were true before, you just couldn't see them. .NET 8, 9 and 10. .NET 6 support is gone. The replicator has also had a lot of work done to it. Taxonomy replication is handled properly, transient lock failures are retried instead of thrown, and persistent commit failures now surface through IsReplicationHealthy instead of being silently swallowed. If you're running Examine on Azure App Service, or anything else with a network file system, that's the part that matters to you. There are breaking changes and they're mostly constructors. The release notes have a summary and the full API change report lists every one of them. Umbraco and the new search abstraction Umbraco HQ have been rebuilding search from the ground up in Umbraco.Cms.Search, based on the Future of Search RFC written by Bjarke Berg and Kenn Jacobsen. It's a provider based abstraction, so in principle you can put Elasticsearch, Algolia or anything else behind it. For the initial release there is one provider, and it's Examine. From the RFC: We plan to ship a single implementation of the search abstraction, which will be based on Examine to be backward compatible. Their README says it will replace the current search implementation "at the earliest starting from Umbraco v19" so I'm not going to put a date on that, but it ships as an add-on for v17 and v18 first and Examine is what sits underneath it either way. Which meant that v4 suddenly had a real consumer with real deadlines, and a beta wasn't going to cut it any more. It's a nice position to be in though - Examine has been quietly doing its job under Umbraco for years, and it's great to see it become the foundation for the new search stack too. Backwards compatibility Most of the work in the final stretch wasn't features at all, it was making sure v4 could be a drop-in replacement for v3. Examine underpins search in every Umbraco install, so telling everyone to upgrade and fix up their code isn't a realistic answer. I spent a good while on this with Kenn Jacobsen and Nikolaj Geisle at Umbraco HQ - they would run v4 against the CMS, something would break, and we'd work out whether it was a genuine bug, an API I'd removed too eagerly, or something that needed a compatibility shim. Then repeat. There was a lot of testing on both sides of this. A good example of the kind of thing that catches you out: I had removed some old positional overloads of AddExamineLuceneIndex and replaced them with optional parameters. That's source compatible, so anything recompiling against v4 is fine. But optional parameters are a compile time feature, and Umbraco's already published DLLs still had IL referencing the original method signatures, so it fell over on startup with a MissingMethodException. Source compatible, binary incompatible, and the only way to find it is to actually run it. Automating the compatibility testing Doing that verification manually over and over is pretty tedious, so I stopped doing it manually. I wrote a set of Copilot skills that live in the Examine repo and automate the whole loop - clone the downstream consumer, rewrite its Examine NuGet package references into project references pointing at my local working copy, build it, run its tests, and report back exactly what broke. There's one each for Umbraco CMS, Umbraco.Cms.Search and ExamineX, plus a compat validator agent that picks the right one and drives it. That turned "have I broken Umbraco?" from an afternoon's work into something I could run as a routine check while working on a branch. Combined with the Roslyn public API analyzer, which tracks every public member in a checked-in file and fails the build when one disappears, breaking changes had to be a deliberate decision rather than something discovered later by somebody else. If you maintain a library with downstream consumers you care about, this is honestly worth setting up. It's a lot more approachable to build than it was even a year ago, and it takes a whole category of worry off your plate. Umbraco.Cms.Search is currently pinned to 4.0.0-beta.9 and can now move to the final release. Documentation The documentation site hadn't been published since March 2023. Not for lack of anyone writing any - the publishing pipeline itself had quietly been broken for years. It only triggered on a branch nobody was pushing to, it used versions of the GitHub Pages actions that have since been removed, and it built with .NET 6, which can't compile a project targeting .NET 8 or later. So documentation kept getting written and committed and never actually went anywhere. That's all fixed now and the docs rebuild and publish automatically on every push, which means about three years of writing has gone live in one go. Faceting, taxonomy indexes, deep paging and replication are all documented, so it's well worth a look if the last version you saw was the old site. A number of the older code samples turned out to be wrong as well, including every faceting example, which called a method that doesn't exist. This time around every sample was compiled against the real v4 assemblies rather than just eyeballed, which is how those got caught. The public API change report is published there too - 246 additions, 19 changed signatures and 14 removals between v3.10.0 and v4.0.0. It's generated from the API surface files that the analyzer keeps in the repo rather than written by hand, so it's the complete picture rather than what I remembered to write down. What this means for ExamineX I've been asked this a few times now so it's worth spelling out. ExamineX is my commercial product that swaps Examine's Lucene implementation for Azure AI Search or Elasticsearch, so that you don't have to deal with file locks, index corruption and index rebuilds on Azure App Service. It has always been an implementation of the Examine abstractions rather than a fork or a competing API. That's the important part here. Umbraco Search has a provider model and its default provider talks to Examine. Examine has its own implementation model underneath that, and that's the layer ExamineX plugs into. They're two separate seams stacked on top of each other, so ExamineX continues to work under Umbraco Search without needing to be an Umbraco Search provider at all - it just replaces what sits underneath the Examine one. So the new Umbraco search stack doesn't replace ExamineX, it sits on top of the same abstraction it always has. The v4 support including faceting is already written, I just haven't cut the release yet - that's next on my list. Upgrading If you're on v3 and not using Umbraco then it should be reasonably straight forward. Retarget to net8.0 or later, rebuild, work through whatever the nullable annotations tell you, and check the API change report if you were constructing searchers or directory factories by hand. If you're on Umbraco, wait for Umbraco to pull it in rather than upgrading Examine underneath it yourself. Everything is on GitHub and issues and PRs are always welcome. Give it a go! Huge thanks to Chad, both Nikolajs and Kenn, and to everyone who ran the betas in production and told me what broke. Seventeen years in and Examine is in the best shape it's ever been, which is a pretty good feeling 🙂

by Shanon Thompson (Deminick)

Examine v4 is out

Examine v4 is out Examine v4.0.0 has been released to NuGet! It's been in beta since October 2023, so it feels great to finally have this one out the door. The headline feature is faceting, which people have been asking me about for years, along with taxonomy indexes, deep paging, and a fair amount of modernization of the codebase. What took so long The first facet commit landed in November 2022 from Nikolaj Brask-Nielsen who did the original implementation, and then Chad Currie built taxonomy index support, deep paging and a lot of the surrounding infrastructure on top of that. Between them that's well over a hundred commits of genuinely hard Lucene work, and honestly the community contributions on this release have been fantastic. After that it sat in beta across nine releases. Part of that is simply that Examine is a side project I maintain around a full time job, and nothing was broken enough in the betas to force the issue. The other part is that Examine v4 is built on Lucene.NET 4.8, which is itself still in beta, and I didn't want to ship a final release that depended on a beta of something else. So the plan was to wait for Lucene.NET 4.8 to go stable first. Lucene.NET 4.8.0-beta00001 was released in May 2017, and the last stable Lucene.NET release was 3.0.3 back in 2012, so in hindsight that was a fairly optimistic plan 😊 That's not a criticism by the way - porting Lucene to .NET is an enormous amount of work done by volunteers and the project is very much active. beta00018 shipped in June this year and the hope is for a final 4.8 release before the end of the year. In practice these betas have been running in production on a very large number of Umbraco sites for years now, so the beta label was never really the risk that it sounds like. What changed is that Umbraco needed v4. What's new in v4 Faceting is the main one. You declare facet fields in your field definitions like any other field, request facets on a query with WithFacets(...), and read them back with GetFacet(): var results = searcher.CreateQuery("content") .Field("nodeName", "product") .WithFacets(facets => facets.FacetString("category")) .Execute(); foreach (var value in results.GetFacet("category")!) { Console.WriteLine($"{value.Label}: {value.Value}"); } There are facet types for full text, numerics, dates and ranges, and each has a taxonomy variant. Being able to do this natively in Examine, rather than dropping down to Lucene yourself, is the bit I'm most pleased about in this release. The rest of it: Taxonomy indexes - a sidecar index enabled with UseTaxonomyIndex, which gives you hierarchical facets and faster faceting generally. This started out as a separate index type and ended up merged into LuceneIndex, which is a much better place for it. Deep paging - SearchAfterOptions lets you page a long way into a result set without Lucene having to materialize everything up to your offset. It works with faceted queries too. Boost factors and phrase queries - WithBoost() on individual search values, and a proper Phrase() instead of the old escaped value approach. Nullable reference types throughout, with warnings as errors, so every public API is annotated. You'll likely get some new warnings when you upgrade, all of which were true before, you just couldn't see them. .NET 8, 9 and 10. .NET 6 support is gone. The replicator has also had a lot of work done to it. Taxonomy replication is handled properly, transient lock failures are retried instead of thrown, and persistent commit failures now surface through IsReplicationHealthy instead of being silently swallowed. If you're running Examine on Azure App Service, or anything else with a network file system, that's the part that matters to you. There are breaking changes and they're mostly constructors. The release notes have a summary and the full API change report lists every one of them. Umbraco and the new search abstraction Umbraco HQ have been rebuilding search from the ground up in Umbraco.Cms.Search, based on the Future of Search RFC written by Bjarke Berg and Kenn Jacobsen. It's a provider based abstraction, so in principle you can put Elasticsearch, Algolia or anything else behind it. For the initial release there is one provider, and it's Examine. From the RFC: We plan to ship a single implementation of the search abstraction, which will be based on Examine to be backward compatible. Their README says it will replace the current search implementation "at the earliest starting from Umbraco v19" so I'm not going to put a date on that, but it ships as an add-on for v17 and v18 first and Examine is what sits underneath it either way. Which meant that v4 suddenly had a real consumer with real deadlines, and a beta wasn't going to cut it any more. It's a nice position to be in though - Examine has been quietly doing its job under Umbraco for years, and it's great to see it become the foundation for the new search stack too. Backwards compatibility Most of the work in the final stretch wasn't features at all, it was making sure v4 could be a drop-in replacement for v3. Examine underpins search in every Umbraco install, so telling everyone to upgrade and fix up their code isn't a realistic answer. I spent a good while on this with Kenn Jacobsen and Nikolaj Geisle at Umbraco HQ - they would run v4 against the CMS, something would break, and we'd work out whether it was a genuine bug, an API I'd removed too eagerly, or something that needed a compatibility shim. Then repeat. There was a lot of testing on both sides of this. A good example of the kind of thing that catches you out: I had removed some old positional overloads of AddExamineLuceneIndex and replaced them with optional parameters. That's source compatible, so anything recompiling against v4 is fine. But optional parameters are a compile time feature, and Umbraco's already published DLLs still had IL referencing the original method signatures, so it fell over on startup with a MissingMethodException. Source compatible, binary incompatible, and the only way to find it is to actually run it. Automating the compatibility testing Doing that verification manually over and over is pretty tedious, so I stopped doing it manually. I wrote a set of Copilot skills that live in the Examine repo and automate the whole loop - clone the downstream consumer, rewrite its Examine NuGet package references into project references pointing at my local working copy, build it, run its tests, and report back exactly what broke. There's one each for Umbraco CMS, Umbraco.Cms.Search and ExamineX, plus a compat validator agent that picks the right one and drives it. That turned "have I broken Umbraco?" from an afternoon's work into something I could run as a routine check while working on a branch. Combined with the Roslyn public API analyzer, which tracks every public member in a checked-in file and fails the build when one disappears, breaking changes had to be a deliberate decision rather than something discovered later by somebody else. If you maintain a library with downstream consumers you care about, this is honestly worth setting up. It's a lot more approachable to build than it was even a year ago, and it takes a whole category of worry off your plate. Umbraco.Cms.Search is currently pinned to 4.0.0-beta.9 and can now move to the final release. Documentation The documentation site hadn't been published since March 2023. Not for lack of anyone writing any - the publishing pipeline itself had quietly been broken for years. It only triggered on a branch nobody was pushing to, it used versions of the GitHub Pages actions that have since been removed, and it built with .NET 6, which can't compile a project targeting .NET 8 or later. So documentation kept getting written and committed and never actually went anywhere. That's all fixed now and the docs rebuild and publish automatically on every push, which means about three years of writing has gone live in one go. Faceting, taxonomy indexes, deep paging and replication are all documented, so it's well worth a look if the last version you saw was the old site. A number of the older code samples turned out to be wrong as well, including every faceting example, which called a method that doesn't exist. This time around every sample was compiled against the real v4 assemblies rather than just eyeballed, which is how those got caught. The public API change report is published there too - 246 additions, 19 changed signatures and 14 removals between v3.10.0 and v4.0.0. It's generated from the API surface files that the analyzer keeps in the repo rather than written by hand, so it's the complete picture rather than what I remembered to write down. What this means for ExamineX I've been asked this a few times now so it's worth spelling out. ExamineX is my commercial product that swaps Examine's Lucene implementation for Azure AI Search or Elasticsearch, so that you don't have to deal with file locks, index corruption and index rebuilds on Azure App Service. It has always been an implementation of the Examine abstractions rather than a fork or a competing API. That's the important part here. Umbraco Search has a provider model and its default provider talks to Examine. Examine has its own implementation model underneath that, and that's the layer ExamineX plugs into. They're two separate seams stacked on top of each other, so ExamineX continues to work under Umbraco Search without needing to be an Umbraco Search provider at all - it just replaces what sits underneath the Examine one. So the new Umbraco search stack doesn't replace ExamineX, it sits on top of the same abstraction it always has. The v4 support including faceting is already written, I just haven't cut the release yet - that's next on my list. Upgrading If you're on v3 and not using Umbraco then it should be reasonably straight forward. Retarget to net8.0 or later, rebuild, work through whatever the nullable annotations tell you, and check the API change report if you were constructing searchers or directory factories by hand. If you're on Umbraco, wait for Umbraco to pull it in rather than upgrading Examine underneath it yourself. Everything is on GitHub and issues and PRs are always welcome. Give it a go! Huge thanks to Chad, both Nikolajs and Kenn, and to everyone who ran the betas in production and told me what broke. Seventeen years in and Examine is in the best shape it's ever been, which is a pretty good feeling 🙂

by Shanon Thompson (Deminick)

Upgrading Umbraco 13 to 17: What We Learned

What we learned upgrading a large production website from Umbraco 13 to 17, covering migrations, APIs, AngularJS, property editors, Forms, content and deployment.

by David Whalley

Umbraco on DDEV: .NET, SQL Server, and the generic project type

An experiment in running Umbraco CMS and .NET 10 under DDEV, using the generic project type, a custom web image, and Azure SQL Edge.

by Lee Mills