<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="/firehose/news?altTemplate=FirehoseXslt" type="text/xsl" media="screen" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:umb="http://www.umbraco.com" umb:media-type="news">
    <channel>
        <atom:link href="https://umb.fyi/firehose/news" rel="self" type="application/rss+xml" />
        <title>UMB.FYI Firehose - News</title>
        <link>https://umb.fyi/firehose/news</link>
        <description>Aggregated news from the Umbraco community</description>
        <pubDate>Tue, 24 Oct 2023 14:27:33 Z</pubDate>
        <lastBuildDate>Fri, 10 Apr 2026 09:57:56 Z</lastBuildDate>
        <language>en</language>
        <generator>Umbraco</generator>
                <item>
                    <guid>https://marketplace.umbraco.com/package/proworks.umbraco.ai.pageevaluator</guid>
                    <title>ProWorks.Umbraco.AI.PageEvaluator</title>
                    <link>https://marketplace.umbraco.com/package/proworks.umbraco.ai.pageevaluator</link>
                    <description><![CDATA[An Umbraco 17 backoffice package that adds an Evaluate Page button to the content editor toolbar. When clicked, it sends the current page&#x27;s content to an AI model and returns a structured quality report directly inside the backoffice, including scored checks, warnings, and actionable suggestions.]]></description>
                    <pubDate>Fri, 10 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/file-uploads-in-umbraco-ai</guid>
                    <title>File Uploads in Umbraco.AI</title>
                    <link>https://mattbrailsford.dev/file-uploads-in-umbraco-ai</link>
                    <description><![CDATA[File uploads have landed in Umbraco.AI. While the most visible change is in Copilot &#x2014; where you can now drag and drop documents directly into the...]]></description>
                    <pubDate>Thu, 09 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.community.tags.popular</guid>
                    <title>Our.Community.Tags.Popular</title>
                    <link>https://marketplace.umbraco.com/package/our.community.tags.popular</link>
                    <description><![CDATA[A custom property editor for Umbraco 17 that displays the most-used tags in a named group and lets editors multi-select them to populate a sibling Tags property]]></description>
                    <pubDate>Thu, 09 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=mW76ASdqCDs</guid>
                    <title>Umbraco Cloud testimonials</title>
                    <link>https://www.youtube.com/watch?v=mW76ASdqCDs</link>
                    <description><![CDATA[&quot;We use Umbraco Cloud for almost all our clients&#x2014;about 95% of them. The baseline functionality means we can spin up a site pretty quickly and get going fast, which reduces project time and budget while bringing more value to the customers.&quot;&#xA;&#xA;Find out more about Umbraco Cloud here: https://umbraco.com/products/umbraco-cloud/]]></description>
                    <pubDate>Wed, 08 Apr 2026 12:24:47 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://github.com/ProWorksCorporation/ProWorks-Umbraco.AI-Page-Evaluator</guid>
                    <title>ProWorks Umbraco AI Page Evaluator</title>
                    <link>https://github.com/ProWorksCorporation/ProWorks-Umbraco.AI-Page-Evaluator</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 08 Apr 2026 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://www.nevitech.co.uk/blog/indexing-word-excel-and-powerpoint-documents-in-umbraco-using-examine/</guid>
                    <title>Indexing Word, Excel and PowerPoint Documents in Umbraco Using Examine</title>
                    <link>https://www.nevitech.co.uk/blog/indexing-word-excel-and-powerpoint-documents-in-umbraco-using-examine/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 08 Apr 2026 05:28:42 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/umbraco.com/post/3mivnuhezvy2d</guid>
                    <title>Meet the nominees for the Umbraco Awards 2026!</title>
                    <link>https://bsky.app/profile/umbraco.com/post/3mivnuhezvy2d</link>
                    <description><![CDATA[&#x1F680; Meet the nominees for the Umbraco Awards 2026! From stunning design to AI innovation and cloud solutions, these projects showcase the best of our community &#x1F3C6;&#xA;Check them out: umbra.co/3Oq6hY3&#xA;&#xA;#Umbraco&#xA;&#xA;]]></description>
                    <pubDate>Tue, 07 Apr 2026 14:43:09 Z</pubDate>
                        <category>bluesky</category>
                        <category>social</category>
                        <category>hq</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/why-umbraco-1740-is-a-big-deal-for-ai</guid>
                    <title>Why Umbraco 17.4.0 Is a Big Deal for AI</title>
                    <link>https://mattbrailsford.dev/why-umbraco-1740-is-a-big-deal-for-ai</link>
                    <description><![CDATA[If you told most Umbraco developers to get excited about a minor release, you&#x2019;d probably get a polite nod. Point releases aren&#x2019;t usually the ones...]]></description>
                    <pubDate>Tue, 07 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/announcing-the-2026-umbraco-awards-nominees/</guid>
                    <title>Announcing the 2026 Umbraco Awards Nominees &#x1F3C6;</title>
                    <link>https://umbraco.com/blog/announcing-the-2026-umbraco-awards-nominees/</link>
                    <description><![CDATA[The jury has nailed it down to the very best of each category - check out the impressive Umbraco Award 2026 Nominees &#x1F3C6;]]></description>
                    <pubDate>Tue, 07 Apr 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#126</guid>
                    <title>Skrift Issue #126</title>
                    <link>https://skrift.io/issues/#126</link>
                    <description><![CDATA[Featuring guest posts by Bernadet Goey on &quot;Admin Only property using Granular Permissions in Umbraco 17&quot; and Dirk Seefeld on &quot;PostgreSQL Support for Umbraco&quot;]]></description>
                    <pubDate>Tue, 07 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.schema2yaml</guid>
                    <title>SplatDev.Umbraco.Plugins.Schema2Yaml</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.schema2yaml</link>
                    <description><![CDATA[An Umbraco plugin that exports your existing Umbraco schema and content to YAML format. Exports DataTypes, DocumentTypes, Media Types, Templates, Content, Media, Languages, Dictionary Items, Members, and Users to a declarative Infrastructure-as-Code YAML file. Supports Umbraco 13&#x2013;17.]]></description>
                    <pubDate>Sun, 05 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.4</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.4</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.4</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Fri, 03 Apr 2026 23:19:02 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/116335310919405813</guid>
                    <title>Umbraco Spark 2026 is a wrap!</title>
                    <link>https://umbracocommunity.social/@umbracospark/116335310919405813</link>
                    <description><![CDATA[Umbraco Spark 2026 is a wrap! &#x26A1;What a fantastic day &#x2014; full of inspiring sessions, thoughtful conversations and everything that makes this community so special.Huge thanks to everyone who made it happen &#x1F49B;&#x1F3A5; Highlights video attached &#x2014; take a look!&#x1F4DD; Plus, all the conference buzz&#x2014;read our blog: https://gibe.digital/blog/spark-2026-highlights-code-community-creativity/ &#x1F4C5; Save the date: Friday 19th March 2027&#x1F39F; Tickets go on sale November 2026#umbracospark #umbraco #community #webevents #opensource]]></description>
                    <pubDate>Thu, 02 Apr 2026 13:20:50 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/umbraco-ai-prompts-template-syntax-and-variables</guid>
                    <title>Umbraco AI Prompts: Template Syntax and Variables</title>
                    <link>https://mattbrailsford.dev/umbraco-ai-prompts-template-syntax-and-variables</link>
                    <description><![CDATA[Following on from my Introducing Umbraco AI post, I wanted to dig deeper into one of the add-on packages that I think has the most immediately...]]></description>
                    <pubDate>Thu, 02 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/30</guid>
                    <title>[Breaking change]: Casing on EF Core code constructs</title>
                    <link>https://github.com/umbraco/Announcements/issues/30</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 02 Apr 2026 07:34:59 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.postgresql</guid>
                    <title>Our.Umbraco.PostgreSql</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.postgresql</link>
                    <description><![CDATA[Adds support for PostgreSQL to Umbraco CMS.]]></description>
                    <pubDate>Thu, 02 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.examine.openxml</guid>
                    <title>Umbraco.Community.Examine.OpenXml</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.examine.openxml</link>
                    <description><![CDATA[Extracts text content from OpenXml documents (.docx, .pptx, .xlsx) uploaded to the Umbraco media library and indexes it using Examine, making Office document content fully searchable.]]></description>
                    <pubDate>Thu, 02 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://jcdc.dev/blog/umbraco-spark-2026</guid>
                    <title>Umbraco Spark 2026</title>
                    <link>https://jcdc.dev/blog/umbraco-spark-2026</link>
                    <description><![CDATA[My notes and reflections from Umbraco Spark 2026 in Bristol. Covering every talk from Umbraco Search and backoffice collaboration to AI, cloud hosting and community.]]></description>
                    <pubDate>Tue, 31 Mar 2026 21:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/developerlee_merchello-documentation-share-7444728952901877760-Oz7Y/?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</guid>
                    <title>Merchello v17 Starter Site Template now available</title>
                    <link>https://www.linkedin.com/posts/developerlee_merchello-documentation-share-7444728952901877760-Oz7Y/?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</link>
                    <description><![CDATA[Merchello Starter Site Template]]></description>
                    <pubDate>Tue, 31 Mar 2026 13:23:34 Z</pubDate>
                        <category>community</category>
                        <category>linkedin</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/adamshallcross_just-thought-i-would-share-a-sneakpeek-of-ugcPost-7444666108776873986-lvMW/?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAEZMLBQB0B-TxZjsCmPSm4mFHU6Y9pFI12s</guid>
                    <title>AI agent workflows sneak peek</title>
                    <link>https://www.linkedin.com/posts/adamshallcross_just-thought-i-would-share-a-sneakpeek-of-ugcPost-7444666108776873986-lvMW/?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAEZMLBQB0B-TxZjsCmPSm4mFHU6Y9pFI12s</link>
                    <description><![CDATA[Post | LinkedIn]]></description>
                    <pubDate>Tue, 31 Mar 2026 09:35:28 Z</pubDate>
                        <category>social</category>
                        <category>linkedin</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/pulse/finding-my-spark-through-community-spinbox-digital-q5sme/</guid>
                    <title>Finding my Spark through Community</title>
                    <link>https://www.linkedin.com/pulse/finding-my-spark-through-community-spinbox-digital-q5sme/</link>
                    <description><![CDATA[(6) Finding my Spark through Community | LinkedIn]]></description>
                    <pubDate>Tue, 31 Mar 2026 08:55:52 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>linkedin</category>
                </item>
                <item>
                    <guid>https://github.com/Ambertvu/Umbraco.Community.IconPicker/releases/tag/17.0.0</guid>
                    <title>V17 of IconPicker has been released</title>
                    <link>https://github.com/Ambertvu/Umbraco.Community.IconPicker/releases/tag/17.0.0</link>
                    <description><![CDATA[https://github.com/Ambertvu/Umbraco.Community.IconPicker/releases/tag/17.0.0&#xA;V17 of IconPicker has been released. &#xA;- Add an sprite containing all the SVG&#x27;s&#xA;- Pick these in the backoffice&#xA;- Render them as SVG in the frontend, so you can apply styles and effects!]]></description>
                    <pubDate>Tue, 31 Mar 2026 08:26:38 Z</pubDate>
                        <category>package</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.spreaker.com/episode/live-from-spark-2026-ai-play-and-the-human-in-the-loop--71006607</guid>
                    <title>Live from Spark 2026: AI, Play and the Human in the Loop</title>
                    <link>https://www.spreaker.com/episode/live-from-spark-2026-ai-play-and-the-human-in-the-loop--71006607</link>
                    <description><![CDATA[Live from the Umbraco Spark Innovation Conference in Bristol! Emma, Laura and Lotte were joined by Phil Whittaker, AI Staff Engineer at Umbraco HQ - someone who&#x27;s been thinking and talking about AI in development for longer than most. Together we talk about play as a superpower for developer learning, what returning from maternity leave looks like in an AI-transformed workplace, why developers approaching retirement are finding a second wind, and why augmentation beats automation every time.&#xA0;&#xA0;&lt;br /&gt;&#xA0;&#xA0;&lt;br /&gt;A massive thank you to our live audience for missing out on the sunshine and making it so fun to record!&#xA0;&#xA0;&lt;br /&gt;&#xA0;&#xA0;&lt;br /&gt;Links:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Umbraco Spark: &lt;a href=&quot;https://umbracospark.com/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;https://umbracospark.com/&lt;/a&gt; &#xA0;&lt;/li&gt;&lt;li&gt;Phil Whittaker&#x27;s blog: &lt;a href=&quot;https://dev.to/phil-whittaker&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;https://dev.to/phil-whittaker&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;]]></description>
                    <pubDate>Tue, 31 Mar 2026 06:01:17 Z</pubDate>
                        <category>community</category>
                        <category>podcast</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/utpro.feature.filemanager</guid>
                    <title>uTPro.Feature.FileManager</title>
                    <link>https://marketplace.umbraco.com/package/utpro.feature.filemanager</link>
                    <description><![CDATA[A powerful file management dashboard for Umbraco 16&#x2B; backoffice. Browse, upload, download, edit (with Monaco code editor), preview media, rename, delete, extract ZIP, and import files via URL. Features Windows Explorer-style navigation, role-based access control (Admin/Sensitive Data/Media), pagination for large directories, and URL state persistence. Zero configuration required.]]></description>
                    <pubDate>Tue, 31 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/phases.umbraco.community.tiptap.rte.emoji</guid>
                    <title>Phases.Umbraco.Community.Tiptap.Rte.Emoji</title>
                    <link>https://marketplace.umbraco.com/package/phases.umbraco.community.tiptap.rte.emoji</link>
                    <description><![CDATA[Adds a simple emoji picker to the Umbraco TipTap Rich Text Editor. Insert emojis directly into content using a searchable picker in the backoffice.]]></description>
                    <pubDate>Tue, 31 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.tiptap.markdownimport</guid>
                    <title>Umbraco.Community.TipTap.MarkdownImport</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.tiptap.markdownimport</link>
                    <description><![CDATA[Adds a Paste Markdown action to the Umbraco TipTap Rich Text Editor so editors can import Markdown into rich text fields as HTML content.]]></description>
                    <pubDate>Tue, 31 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/optimizing-umbraco-member-pagination-from-slow-to-lightning-fast/</guid>
                    <title>Optimizing Umbraco Member Pagination From Slow to Lightning Fast</title>
                    <link>https://owain.codes/blog/2026/march/optimizing-umbraco-member-pagination-from-slow-to-lightning-fast/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 31 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.datatypecleanup</guid>
                    <title>Umbraco.Community.DataTypeCleanup</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.datatypecleanup</link>
                    <description><![CDATA[Clean up orphan data types, merge duplicates, and organize data types into folders by editor.]]></description>
                    <pubDate>Mon, 30 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/searching-file-content-with-umbraco-search/</guid>
                    <title>Searching file content with Umbraco Search</title>
                    <link>https://kjac.dev/posts/searching-file-content-with-umbraco-search/</link>
                    <description><![CDATA[Introducing a package for searching file content with Umbraco Search.]]></description>
                    <pubDate>Mon, 30 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.yaml2schema</guid>
                    <title>SplatDev.Umbraco.Plugins.Yaml2Schema</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.yaml2schema</link>
                    <description><![CDATA[A declarative, Infrastructure-as-Code style plugin for Umbraco 17 that enables programmatic creation of DataTypes, DocumentTypes, Templates, and Content from a YAML configuration file. Define your entire Umbraco structure in YAML &#x2014; the plugin builds it automatically on startup.]]></description>
                    <pubDate>Mon, 30 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.environmentindicator</guid>
                    <title>Umbraco.Community.EnvironmentIndicator</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.environmentindicator</link>
                    <description><![CDATA[Makes Umbraco backoffice environments stand out with colored favicons and environment name badges in the header.]]></description>
                    <pubDate>Mon, 30 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/Umbraco.Community.ContentLock</guid>
                    <title>Content Lock 17.1.0 - aka the WebRTC Audio Calling edition</title>
                    <link>https://www.nuget.org/packages/Umbraco.Community.ContentLock</link>
                    <description><![CDATA[Content Lock 17.1.0 - aka the WebRTC Audio Calling edition&#xA;Latest release adds in audio calling to other logged in users of the backoffice with nothing to install.&#xA;So that quick call to another editor to hurry up and unlock the homenode so you can get the press release out before lunch can be done without having to remember what Microsoft Teams account you need to login to&#xA;&#xA;Docs Site&#xA;https://warrenbuckley.github.io/Umbraco.Community.ContentLock/&#xA;&#xA;Marketplace&#xA;https://marketplace.umbraco.com/package/umbraco.community.contentlock&#xA;&#xA;Nuget&#xA;https://www.nuget.org/packages/Umbraco.Community.ContentLock]]></description>
                    <pubDate>Fri, 27 Mar 2026 12:30:04 Z</pubDate>
                        <category>package</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=-b3HkTedXcA</guid>
                    <title>umbraCoffee - Spring Sips</title>
                    <link>https://www.youtube.com/watch?v=-b3HkTedXcA</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;This time we&#x27;re joined by Umbraco Hacker, Maker and Do&#x27;er - Warren Buckley - to talk about everything Umbraco from the past moth.&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;// ToDo:]]></description>
                    <pubDate>Fri, 27 Mar 2026 08:51:45 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.propertyinfo</guid>
                    <title>Umbraco.Community.PropertyInfo</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.propertyinfo</link>
                    <description><![CDATA[Package Description]]></description>
                    <pubDate>Fri, 27 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/utpro.feature.auditlog</guid>
                    <title>uTPro.Feature.AuditLog</title>
                    <link>https://marketplace.umbraco.com/package/utpro.feature.auditlog</link>
                    <description><![CDATA[Audit Log viewer dashboard for Umbraco 16&#x2B; backoffice. Browse umbracoAudit and umbracoLog tables with filtering, search, and pagination.]]></description>
                    <pubDate>Fri, 27 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/understanding-contexts-in-umbraco-ai</guid>
                    <title>Understanding Contexts in Umbraco AI</title>
                    <link>https://mattbrailsford.dev/understanding-contexts-in-umbraco-ai</link>
                    <description><![CDATA[In my introductory post, I gave a high-level overview of how Umbraco AI is structured &#x2014; providers, connections, profiles, and contexts. Of those,...]]></description>
                    <pubDate>Fri, 27 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=HLV0FeRKhNI</guid>
                    <title>Early concepts for Reuse Content of Blocks</title>
                    <link>https://www.youtube.com/watch?v=HLV0FeRKhNI</link>
                    <description><![CDATA[(4) Early concepts for Reuse Content of Blocks - YouTube]]></description>
                    <pubDate>Thu, 26 Mar 2026 13:08:18 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/modgift.umbraco.croninput</guid>
                    <title>Modgift.Umbraco.CronInput</title>
                    <link>https://marketplace.umbraco.com/package/modgift.umbraco.croninput</link>
                    <description><![CDATA[A property editor for Umbraco that provides a cron expression input with real-time human-readable descriptions. As the user types a cron expression, a label beneath the input instantly translates it into plain language. Includes an optional validation setting to prevent publishing without a valid expression.]]></description>
                    <pubDate>Thu, 26 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/announcing-the-umbraco-in-ai-community-team-the-ai-in-umbraco-advisory-board/</guid>
                    <title>Announcing the Umbraco in AI Community Team &amp; the AI in Umbraco Advisory Board</title>
                    <link>https://umbraco.com/blog/announcing-the-umbraco-in-ai-community-team-the-ai-in-umbraco-advisory-board/</link>
                    <description><![CDATA[Meet the 27 experts joining the Umbraco in AI Community Team and Advisory Board to help shape the future of AI tools and editor experiences within Umbraco.]]></description>
                    <pubDate>Thu, 26 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nevitech.co.uk/blog/building-an-ai-powered-log-analyser-for-umbraco-at-the-spark-hackathon/</guid>
                    <title>Building an AI-Powered Log Analyser for Umbraco at the Spark Hackathon</title>
                    <link>https://www.nevitech.co.uk/blog/building-an-ai-powered-log-analyser-for-umbraco-at-the-spark-hackathon/</link>
                    <description><![CDATA[A look at how I built AI.LogAnalyser at the  @umbracospark.bsky.social Hackathon - an open-source package that adds one-click AI analysis to the Umbraco backoffice log viewer.  Powered by any AI provider via Umbraco AI.&#xA;&#xA;www.nevitech.co.uk/blog/buildin...&#xA;&#xA;#umbraco #ai #package&#xA;&#xA;]]></description>
                    <pubDate>Wed, 25 Mar 2026 09:50:08 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rescope.contentdelivery</guid>
                    <title>Rescope.ContentDelivery</title>
                    <link>https://marketplace.umbraco.com/package/rescope.contentdelivery</link>
                    <description><![CDATA[Improves swagger definitions for use with typed client generators]]></description>
                    <pubDate>Wed, 25 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.updoc</guid>
                    <title>Umbraco.Community.UpDoc</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.updoc</link>
                    <description><![CDATA[Create Umbraco documents from external sources (PDF, web pages, markdown) using configurable extraction workflows with rule-based field mapping.]]></description>
                    <pubDate>Wed, 25 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rescope.uastro</guid>
                    <title>Rescope.uAstro</title>
                    <link>https://marketplace.umbraco.com/package/rescope.uastro</link>
                    <description><![CDATA[Automatically build and serve static Astro sites within Umbraco]]></description>
                    <pubDate>Wed, 25 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/metamateai</guid>
                    <title>MetaMateAI</title>
                    <link>https://marketplace.umbraco.com/package/metamateai</link>
                    <description><![CDATA[Umbraco plugin for generating creative meta title and meta description values for pages with missing metadata.]]></description>
                    <pubDate>Tue, 24 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/gitkraken-cli-beginners-guide/</guid>
                    <title>GitKraken CLI Beginner&#x27;s Guide: A Better Git CLI for Developers</title>
                    <link>https://owain.codes/blog/2026/march/gitkraken-cli-beginners-guide/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 24 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nathanielnunes.com/blog/umbraco-in-action-introduction-to-cms-for-students</guid>
                    <title>Umbraco In Action - Introduction to CMS for students</title>
                    <link>https://www.nathanielnunes.com/blog/umbraco-in-action-introduction-to-cms-for-students</link>
                    <description><![CDATA[Just put out a blog &#x2B; session recording from a talk I did with Coders Club students at Don Bosco College of Engineering, Fatorda.&#xA;&#xA;Started simple with &#x201C;what is a CMS&#x201D; before jumping into demos with Umbraco, headless setup with Next.js, and even a bit of AI.&#xA;&#xA;Good questions from the students, especially around WordPress vs Umbraco &#x1F440;&#xA;&#xA;Blog Post: https://www.nathanielnunes.com/blog/umbraco-in-action-introduction-to-cms-for-students&#xA;Video: https://youtu.be/znH8tnedIQg]]></description>
                    <pubDate>Mon, 23 Mar 2026 06:57:07 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/conzent.umbraco</guid>
                    <title>Conzent.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/conzent.umbraco</link>
                    <description><![CDATA[GDPR, CCPA &amp; ePrivacy compliant cookie consent banner for Umbraco. IAB TCF v2.3 and Google CMP Certified. Supports Conzent Cloud and self-hosted OCI.]]></description>
                    <pubDate>Mon, 23 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/modgift.umbraco.datagrid</guid>
                    <title>Modgift.Umbraco.DataGrid</title>
                    <link>https://marketplace.umbraco.com/package/modgift.umbraco.datagrid</link>
                    <description><![CDATA[A multiple column data editor property editor for Umbraco with strongly-typed DataGrid model.]]></description>
                    <pubDate>Sat, 21 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.deliveryapimodelmapper</guid>
                    <title>Umbraco.Community.DeliveryApiModelMapper</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.deliveryapimodelmapper</link>
                    <description><![CDATA[This package allows you to create custom models which will get ouput in the Delivery API response as a custom &quot;model&quot; property.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/growcreate.schemagenerator.deployaddon</guid>
                    <title>Growcreate.SchemaGenerator.DeployAddon</title>
                    <link>https://marketplace.umbraco.com/package/growcreate.schemagenerator.deployaddon</link>
                    <description><![CDATA[Schema Generator for Umbraco to create and render schema objects. Map document type properties to schema properties and allow editors to populate additional schemas.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.cleanup</guid>
                    <title>Jumoo.TranslationManager.Cleanup</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.cleanup</link>
                    <description><![CDATA[Prodvides health checks to clean up archived jobs and nodes.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/growcreate.schemagenerator.usyncaddon</guid>
                    <title>Growcreate.SchemaGenerator.uSyncAddon</title>
                    <link>https://marketplace.umbraco.com/package/growcreate.schemagenerator.usyncaddon</link>
                    <description><![CDATA[Schema Generator for Umbraco to create and render schema objects. Map document type properties to schema properties and allow editors to populate additional schemas.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.ai.loganalyser</guid>
                    <title>Umbraco.Community.AI.LogAnalyser</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ai.loganalyser</link>
                    <description><![CDATA[Adds AI-powered log analysis to the Umbraco backoffice log viewer. Analyse any log entry with one click using your configured AI provider via Umbraco.AI.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/growcreate.schemagenerator.core</guid>
                    <title>Growcreate.SchemaGenerator.Core</title>
                    <link>https://marketplace.umbraco.com/package/growcreate.schemagenerator.core</link>
                    <description><![CDATA[Schema Generator for Umbraco to create and render schema objects. Map document type properties to schema properties and allow editors to populate additional schemas.]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.debasish.tech/blogs/developer-s-guide-to-creating-a-custom-umbraco-17-backoffice-dashboard/</guid>
                    <title>Developer&#x2019;s Guide to Creating a Custom Umbraco 17 Backoffice Dashboard</title>
                    <link>https://www.debasish.tech/blogs/developer-s-guide-to-creating-a-custom-umbraco-17-backoffice-dashboard/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 20 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/cultiv/setting-up-a-new-umbraco-package-dev-environment-with-umbracoai-3iac</guid>
                    <title>Setting Up a New Umbraco Package Dev Environment with Umbraco.AI</title>
                    <link>https://dev.to/cultiv/setting-up-a-new-umbraco-package-dev-environment-with-umbracoai-3iac</link>
                    <description><![CDATA[&lt;p&gt;This guide walks through setting up a new Umbraco package project called &lt;code&gt;Umbraco.Community.AI.Woowoo&lt;/code&gt; that extends &lt;a href=&quot;https://umbraco.com/marketplace/category/ai/&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco.AI&lt;/a&gt;. Rather than starting from scratch with a blank solution, the goal is a proper package structure from day one: the right project layout, a frontend build pipeline, uSync, GitHub Actions for NuGet publishing, the works.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That&#x27;s where &lt;a href=&quot;https://github.com/LottePitcher/opinionated-package-starter&quot; rel=&quot;noopener noreferrer&quot;&gt;Lotte&#x27;s Opinionated Package Starter Template&lt;/a&gt; comes in. Lotte has done the hard work of figuring out what a well-structured Umbraco package solution should look like, and turned it into a &lt;code&gt;dotnet new&lt;/code&gt; template. Pair that with Matt Brailsford&#x27;s &lt;a href=&quot;https://mattbrailsford.dev/umbraco-ai-kitchen-sink-install&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco.AI Kitchen Sink install guide&lt;/a&gt;, and you have everything you need to go from zero to a fully wired-up Umbraco.AI development environment.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This post walks through the full setup, including the Linux-specific gotcha to watch out for.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Prerequisites&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;.NET 10 SDK&lt;/strong&gt; -- Umbraco 17 targets .NET 10&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Node.js and npm&lt;/strong&gt; -- the package project uses Vite and TypeScript for its frontend&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;A GitHub account&lt;/strong&gt; -- the template wires up a GitHub remote and includes GitHub Actions for NuGet publishing&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 1: Install the template&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Install the package starter template from NuGet:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;dotnet new &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Umbraco.Community.Templates.PackageStarter&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You should see output ending with something like:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;Success: Umbraco.Community.Templates.PackageStarter::X.X.X installed the following templates:&#xA;  umbracopackagestarter&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;What does this template give you?&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When you scaffold from it, you get a complete two-project solution:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;src/AI.Woowoo/&lt;/code&gt; -- the package project, with Vite &#x2B; TypeScript frontend (via the &lt;code&gt;umbraco-extension&lt;/code&gt; template) and a NuGet-configured &lt;code&gt;.csproj&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;src/AI.Woowoo.TestSite/&lt;/code&gt; -- an Umbraco 17 test site with uSync pre-configured&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;src/AI.Woowoo.sln&lt;/code&gt; -- the solution file, with the TestSite already referencing the package project&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;.github/workflows/release.yml&lt;/code&gt; -- a GitHub Action that publishes to NuGet when you push a version tag&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;umbraco-marketplace.json&lt;/code&gt;, README stubs, issue templates, and an MIT &lt;code&gt;LICENSE&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;That&#x27;s a solid foundation for package development, covering all the scaffolding that would otherwise take hours to set up from scratch.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 2: Scaffold the solution&#xA;&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;dotnet new umbracopackagestarter &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; AI.Woowoo &lt;span class=&quot;nt&quot;&gt;-an&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your Name&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-gu&lt;/span&gt; YourGitHubUsername &lt;span class=&quot;nt&quot;&gt;-gr&lt;/span&gt; Umbraco.Community.AI.Woowoo &lt;span class=&quot;nt&quot;&gt;--allow-scripts&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;yes&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;What each parameter does:&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;Parameter&lt;/th&gt;&#xA;&lt;th&gt;What it does&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;-n AI.Woowoo&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;The base name for the solution. The template prepends &lt;code&gt;Umbraco.Community.&lt;/code&gt;, so the NuGet package ID becomes &lt;code&gt;Umbraco.Community.AI.Woowoo&lt;/code&gt;&#xA;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;-an &quot;Your Name&quot;&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Author name, used in the &lt;code&gt;.csproj&lt;/code&gt; and marketplace metadata&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;-gu YourGitHubUsername&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Your GitHub username, used to construct the repo URL&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;-gr Umbraco.Community.AI.Woowoo&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;The GitHub repository name&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;--allow-scripts yes&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Bypasses the security prompt for the post-creation script (&lt;code&gt;setup.cmd&lt;/code&gt;), which runs &lt;code&gt;npm install&lt;/code&gt; as part of the &lt;code&gt;umbraco-extension&lt;/code&gt; template setup&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;This will take a few minutes -- &lt;code&gt;npm install&lt;/code&gt; runs as part of the setup. Wait for the &quot;All done!&quot; message.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 3: The Linux/macOS gotcha -- &lt;code&gt;setup.cmd&lt;/code&gt; won&#x27;t run&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;If you&#x27;re on Windows, you can skip this section.&lt;/strong&gt; The template&#x27;s &lt;code&gt;setup.cmd&lt;/code&gt; script will have run fine and your solution is ready. Jump straight to Step 4.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;On Linux and macOS, the &lt;code&gt;setup.cmd&lt;/code&gt; post-creation script fails with &quot;Permission denied&quot; because it&#x27;s a Windows batch file. The template &lt;em&gt;files&lt;/em&gt; are all created correctly -- the script just doesn&#x27;t run, so the git repository, the frontend scaffolding, the solution wiring, and a few other things need to be done manually.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s everything the script would have done, translated into commands you can run:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Initialise the git repository&lt;/span&gt;&#xA;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;AI.Woowoo&#xA;git init&#xA;git branch &lt;span class=&quot;nt&quot;&gt;-M&lt;/span&gt; main&#xA;git remote add origin https://github.com/YourGitHubUsername/Umbraco.Community.AI.Woowoo.git&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# Make sure you have the latest Umbraco dotnet templates&lt;/span&gt;&#xA;dotnet new &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Umbraco.Templates &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# Scaffold the package project inside src/&lt;/span&gt;&#xA;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;src&#xA;dotnet new umbraco-extension &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AI.Woowoo&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--site-domain&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://localhost:44361&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--include-example&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# The template creates two .csproj files -- swap them so the NuGet-configured one is active&lt;/span&gt;&#xA;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;AI.Woowoo&#xA;&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;AI.Woowoo.csproj&#xA;&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;AI.Woowoo_nuget.csproj AI.Woowoo.csproj&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# Wire everything up in the solution&lt;/span&gt;&#xA;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..&#xA;dotnet sln add &lt;span class=&quot;s2&quot;&gt;&quot;AI.Woowoo&quot;&lt;/span&gt;&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;AI.Woowoo.TestSite/AI.Woowoo.TestSite.csproj&quot;&lt;/span&gt; reference &lt;span class=&quot;s2&quot;&gt;&quot;AI.Woowoo/AI.Woowoo.csproj&quot;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The repo root also contains a &lt;code&gt;setup.mjs&lt;/code&gt; file -- a Node.js replacement for &lt;code&gt;setup.cmd&lt;/code&gt; that works cross-platform. If it&#x27;s present, running &lt;code&gt;node setup.mjs&lt;/code&gt; handles all the steps above.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 4: Build the frontend &amp;amp; verify the base site&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before layering anything on top, confirm the scaffolded solution actually works. Build the frontend assets first:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;src/AI.Woowoo/Client&#xA;npm &lt;span class=&quot;nb&quot;&gt;install&#xA;&lt;/span&gt;npm run build&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You should see Vite output with files written to &lt;code&gt;dist/&lt;/code&gt;. Now start the TestSite:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../../AI.Woowoo.TestSite&#xA;dotnet run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;launchSettings.json&lt;/code&gt; included by the template automatically sets &lt;code&gt;ASPNETCORE_ENVIRONMENT=Development&lt;/code&gt;, so you don&#x27;t need to set it yourself. On first run, Umbraco installs itself unattended -- watch the terminal output for the URL (typically &lt;code&gt;https://localhost:44331&lt;/code&gt; or similar).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Log in with the pre-configured credentials from &lt;code&gt;appsettings.json&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Email:&lt;/strong&gt; &lt;code&gt;admin@example.com&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;1234567890&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Check two things:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Navigate to the &lt;strong&gt;Content&lt;/strong&gt; section -- an &quot;Example Dashboard&quot; should be there (this is the example added by the &lt;code&gt;--include-example&lt;/code&gt; flag in the &lt;code&gt;umbraco-extension&lt;/code&gt; template)&lt;/li&gt;&#xA;&lt;li&gt;Navigate to &lt;code&gt;/umbraco/swagger&lt;/code&gt; -- the &lt;strong&gt;Umbraco Management API&lt;/strong&gt; document should appear in the dropdown&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;Once everything looks good, hit &lt;code&gt;Ctrl&#x2B;C&lt;/code&gt; to stop the site. You now have a clean, working base to build on.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 5: Add the Umbraco.AI packages&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;All the Umbraco.AI packages go into the TestSite project, not the package project. The package project stays clean -- it will only reference &lt;code&gt;Umbraco.AI&lt;/code&gt; (or specific Umbraco.AI interfaces) when actually needed for package code. The TestSite is the development host.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Run these from the repo root (&lt;code&gt;AI.Woowoo/&lt;/code&gt;):&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# The Clean starter kit adds a demo content structure to the site&lt;/span&gt;&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Clean&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# The core Umbraco.AI integration layer&lt;/span&gt;&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# Addons&lt;/span&gt;&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Prompt&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Agent&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Agent.Copilot &lt;span class=&quot;nt&quot;&gt;--prerelease&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c&quot;&gt;# AI providers&lt;/span&gt;&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Amazon&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Anthropic&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.Google&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.MicrosoftFoundry&#xA;dotnet add &lt;span class=&quot;s2&quot;&gt;&quot;src/AI.Woowoo.TestSite&quot;&lt;/span&gt; package Umbraco.AI.OpenAI&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;A couple of notes:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;Umbraco.AI.Agent.Copilot&lt;/code&gt; requires &lt;code&gt;--prerelease&lt;/code&gt; because it&#x27;s still in alpha. The other packages are stable.&lt;/li&gt;&#xA;&lt;li&gt;All five provider packages (Amazon Bedrock, Anthropic, Google, Microsoft Foundry, OpenAI) are installed so the environment is ready for any AI provider you or contributors want to test against. The seed data in the next step only activates the OpenAI connection, but the others are configured and waiting.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Check &lt;a href=&quot;https://mattbrailsford.dev/umbraco-ai-kitchen-sink-install&quot; rel=&quot;noopener noreferrer&quot;&gt;Matt&#x27;s install guide&lt;/a&gt; if you&#x27;re following along later -- version numbers may have moved on. The &lt;a href=&quot;https://github.com/umbraco/Umbraco.AI/tree/main/docs/public&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco.AI documentation&lt;/a&gt; covers all the extensibility points in detail if you want to go further.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 6: Add the seed demo data&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Matt has a helpful GitHub Gist that seeds a full Umbraco.AI demo configuration: a connection, profile, context, prompts, and agents. It&#x27;s a self-registering C# file -- drop it in the project and it registers itself via Umbraco&#x27;s component system and seeds the data on first startup. It&#x27;s also idempotent, so safe to leave in place.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; src/AI.Woowoo.TestSite/UmbracoAISeedData.cs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&#xA;  &lt;span class=&quot;s2&quot;&gt;&quot;https://gist.githubusercontent.com/mattbrailsford/199d0b45e926ffb122fa96467039bd90/raw/UmbracoAISeedData.cs?v=2&quot;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  API key (optional)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The seed data installs an OpenAI connection with a dummy key. The site runs fine without a real key -- all the AI configuration gets seeded into the backoffice, the connection just won&#x27;t be functional. Replace the key later through the Umbraco.AI section in the backoffice once everything is up and running.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Don&#x27;t have an OpenAI key? Google hands out free Gemini API keys via &lt;a href=&quot;https://aistudio.google.com/app/apikey&quot; rel=&quot;noopener noreferrer&quot;&gt;Google AI Studio&lt;/a&gt; -- no credit card required. Hit &quot;Create API key&quot; in the top right, or &quot;Get API key&quot; in the left sidebar. The free tier is generous enough for development. If you go this route, use the &lt;code&gt;Umbraco.AI.Google&lt;/code&gt; provider and configure the connection in the backoffice accordingly.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 7: Run and verify&#xA;&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;src/AI.Woowoo.TestSite&#xA;dotnet run&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Once it&#x27;s running, log in (&lt;code&gt;admin@example.com&lt;/code&gt; / &lt;code&gt;1234567890&lt;/code&gt;) and navigate to the &lt;strong&gt;Umbraco.AI&lt;/strong&gt; section in the backoffice. You should see:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A seeded OpenAI connection&lt;/li&gt;&#xA;&lt;li&gt;A profile&lt;/li&gt;&#xA;&lt;li&gt;A context&lt;/li&gt;&#xA;&lt;li&gt;Several prompts&lt;/li&gt;&#xA;&lt;li&gt;Several agents&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;If you added a real API key, try running a prompt or agent to confirm the connection is live.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Step 8: Make the initial commit&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before staging everything, do a quick sanity check: open &lt;code&gt;UmbracoAISeedData.cs&lt;/code&gt; and confirm there&#x27;s no real API key in there.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;&#xA;git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;chore: initial scaffold from Lotte&#x27;s Opinionated Package Starter Template&quot;&lt;/span&gt;&#xA;git push &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; origin main&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; before pushing, go to GitHub and create a new empty repository named &lt;code&gt;Umbraco.Community.AI.Woowoo&lt;/code&gt; (skip the readme, license, and gitignore options -- the template already created those locally). The template already configured the remote, so &lt;code&gt;git push&lt;/code&gt; goes straight to the right place.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What&#x27;s next&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The environment is now ready:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;src/AI.Woowoo/&lt;/code&gt; -- the package project, clean and ready for package code&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;src/AI.Woowoo.TestSite/&lt;/code&gt; -- a full Umbraco 17 &#x2B; Umbraco.AI host to develop and test against, with demo data seeded&lt;/li&gt;&#xA;&lt;li&gt;GitHub Actions configured for NuGet publishing on version tags&lt;/li&gt;&#xA;&lt;li&gt;All five AI providers installed and ready to configure&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;When it comes to implementing the package itself, the first thing to add to &lt;code&gt;src/AI.Woowoo/AI.Woowoo.csproj&lt;/code&gt; is a reference to &lt;code&gt;Umbraco.AI.Core&lt;/code&gt; -- the package that contains all the extension points (custom tools, middleware, guardrail evaluators, etc.):&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;dotnet add src/AI.Woowoo package Umbraco.AI.Core &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; 1.6.0&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Note that &lt;code&gt;Umbraco.AI.Core&lt;/code&gt; 1.6.0 requires Umbraco.Cms 17.1.0 or higher. The template scaffolds the package project against 17.0.0, so bump the four Umbraco.Cms references in the same file to 17.1.0 when you add this.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The next step is implementing the &lt;code&gt;Umbraco.Community.AI.Woowoo&lt;/code&gt; package itself -- but that&#x27;s a story for another post.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 19 Mar 2026 09:30:57 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.ai.propertyvalidation</guid>
                    <title>Umbraco.Community.AI.PropertyValidation</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ai.propertyvalidation</link>
                    <description><![CDATA[AI-powered property validation for Umbraco CMS using the Umbraco.AI infrastructure.]]></description>
                    <pubDate>Thu, 19 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/kraftvaerk.umbraco.alchemy</guid>
                    <title>Kraftvaerk.Umbraco.Alchemy</title>
                    <link>https://marketplace.umbraco.com/package/kraftvaerk.umbraco.alchemy</link>
                    <description><![CDATA[AI-powered developer assistant for the Umbraco backoffice &#x2014; generates property descriptions, document type descriptions, UFM block labels, and content type icons.]]></description>
                    <pubDate>Thu, 19 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/growcreate.schemagenerator</guid>
                    <title>Growcreate.SchemaGenerator</title>
                    <link>https://marketplace.umbraco.com/package/growcreate.schemagenerator</link>
                    <description><![CDATA[Schema Generator for Umbraco to create and render schema objects. Map document type properties to schema properties and allow editors to populate additional schemas.]]></description>
                    <pubDate>Thu, 19 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.powersort</guid>
                    <title>OC.PowerSort</title>
                    <link>https://marketplace.umbraco.com/package/oc.powersort</link>
                    <description><![CDATA[A powerful sorting extension for Umbraco CMS that provides enhanced sorting capabilities for content nodes in the backoffice.]]></description>
                    <pubDate>Wed, 18 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/ocpowersort-package/</guid>
                    <title>OC.PowerSort package</title>
                    <link>https://owain.codes/blog/2026/march/ocpowersort-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 18 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracoa11y/116246055536313833</guid>
                    <title>The Call for Speakers for UMBRAAD 2026 is open!</title>
                    <link>https://umbracocommunity.social/@umbracoa11y/116246055536313833</link>
                    <description><![CDATA[&#x1F680; The Call for Speakers for UMBRAAD 2026 is open! Take to the virtual stage and present your insights on digital accessibility to the Umbraco community during Global Accessibility Awareness Day. Whether you&#x27;re an experienced presenter or simply passionate about enhancing the web, your contribution is welcome! Key Details:&#x1F5D3;&#xFE0F;  Date: 21st May 2026&#x1F4A1;  Topics: Neurodiversity,  AI &amp; Accessibility, Inclusive UX, Accessibility Fatigue, and more!Submit your session idea: https://sessionize.com/umbraad-2026/]]></description>
                    <pubDate>Tue, 17 Mar 2026 19:27:22 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.nevitech.co.uk/blog/getting-started-with-umbraco-ai/</guid>
                    <title>Getting Started with Umbraco AI</title>
                    <link>https://www.nevitech.co.uk/blog/getting-started-with-umbraco-ai/</link>
                    <description><![CDATA[At last night&#x27;s Umbraco Kent Meetup, I gave a live demo on the new AI integrations in the Umbraco back office. If you missed it - or just want a recap - I&#x27;ve put together a blog post covering everything.&#xA;&#xA;www.nevitech.co.uk/blog/getting...&#xA;&#xA;#umbraco #ai #umbracoai @umb.fyi&#xA;&#xA;]]></description>
                    <pubDate>Tue, 17 Mar 2026 11:56:09 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/ocufmfallbacks-package/</guid>
                    <title>OC.UFMFallbacks Package</title>
                    <link>https://owain.codes/blog/2026/march/ocufmfallbacks-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 17 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.jobsjobsjobs</guid>
                    <title>Umbraco.Community.JobsJobsJobs</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.jobsjobsjobs</link>
                    <description><![CDATA[Backoffice dashboard for recurring Umbraco background jobs with status, manual triggering, persisted run history, cooperative stop support, and opt-in CRON scheduling.]]></description>
                    <pubDate>Tue, 17 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.ufmfallbacks</guid>
                    <title>OC.UFMFallbacks</title>
                    <link>https://marketplace.umbraco.com/package/oc.ufmfallbacks</link>
                    <description><![CDATA[An Umbraco package that extends Umbraco Flavored Markdown (UFM) with a {fbk:} component, giving you property fallbacks and text filters directly inside block labels and other UFM contexts.]]></description>
                    <pubDate>Tue, 17 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-s-spring-2026-g2-highlights-strong-usability-wins-across-headless-dxp-cms-and-webops/</guid>
                    <title>Umbraco Recognized as a G2 Leader Across CMS and Headless in the Spring 2026 Reports</title>
                    <link>https://umbraco.com/blog/umbraco-s-spring-2026-g2-highlights-strong-usability-wins-across-headless-dxp-cms-and-webops/</link>
                    <description><![CDATA[See Umbraco&#x2019;s Winter 2026 G2 results across CMS, Headless, DXP, and WebOps, including key badges, usability gains, and implementation highlights.]]></description>
                    <pubDate>Tue, 17 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=Vn881L24YZU</guid>
                    <title>The Umbraco Playground &#x2014; Experiment, build, and share 1</title>
                    <link>https://www.youtube.com/watch?v=Vn881L24YZU</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 16 Mar 2026 10:04:35 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://richardsoeteman.net/blog/automatically-filter-content-delivery-api-content/</guid>
                    <title>Automatically Filter Content Delivery API Content</title>
                    <link>https://richardsoeteman.net/blog/automatically-filter-content-delivery-api-content/</link>
                    <description><![CDATA[Just Blogged:&#xA;Automatically Filter Content Delivery API Content without applying a filter in querystring parameters..&#xA;https://richardsoeteman.net/blog/automatically-filter-content-delivery-api-content/]]></description>
                    <pubDate>Mon, 16 Mar 2026 09:37:18 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://dev.to/matthewwise/a-practical-guide-to-svgs-in-umbraco-1gm6</guid>
                    <title>A Practical Guide to SVGs in Umbraco</title>
                    <link>https://dev.to/matthewwise/a-practical-guide-to-svgs-in-umbraco-1gm6</link>
                    <description><![CDATA[&lt;p&gt;If you&#x27;ve ever tried to use SVGs properly in Umbraco, you&#x27;ll know it&#x27;s not quite plug-and-play. Icons, logos, illustrations - SVGs are everywhere on the modern web.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Out of the box Umbraco will let you upload SVGs. It even has a dedicated &lt;code&gt;Vector Graphics (SVG)&lt;/code&gt; media type. But there are two things I wanted to improve.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Two Problems&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Inlining for CSS control.&lt;/strong&gt; &lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you serve an SVG as an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, it works - but you lose most of what makes SVGs useful. You can&#x27;t style it with CSS, you can&#x27;t use &lt;code&gt;currentColor&lt;/code&gt; to inherit text colour, you can&#x27;t target individual paths for hover states or animations. To get that level of control you need the SVG markup inlined directly into your HTML.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But inlining creates its own issues. An &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag sandboxes its content - scripts inside the SVG won&#x27;t execute. Inline that same SVG into the page and any &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags or &lt;code&gt;javascript:&lt;/code&gt; handlers will run. An editor could unknowingly upload an SVG containing an XSS attack, and you&#x27;d be serving it straight into the DOM. SVGs from design tools also come with hard-coded &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes that fight your CSS when inlined - you want a &lt;code&gt;viewBox&lt;/code&gt; instead.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Duplicate requests on &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags.&lt;/strong&gt; &lt;br&gt;&#xA;Not every SVG needs to be inlined. But when you call &lt;code&gt;GetCropUrl()&lt;/code&gt; on a media item, Umbraco adds crop and resize parameters to the URL. So &lt;code&gt;GetCropUrl(200)&lt;/code&gt; produces &lt;code&gt;example.svg?width=200&lt;/code&gt;, and &lt;code&gt;GetCropUrl(48)&lt;/code&gt; produces &lt;code&gt;example.svg?width=48&lt;/code&gt;. The browser treats those as different URLs - meaning if you use the same SVG at multiple sizes, it gets downloaded multiple times instead of once. For a vector image that scales to any size, those parameters are pointless and the duplicate requests are pure waste.&lt;/p&gt;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Securing SVG Uploads&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Since we&#x27;re inlining SVGs into the page, we need to make sure they&#x27;re safe before they get anywhere near the DOM.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco has an &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/security/server-side-file-validation#ifilestreamsecurityanalyzer&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;IFileStreamSecurityAnalyzer&lt;/code&gt;&lt;/a&gt; interface that gets called when media is uploaded. Umbraco doesn&#x27;t ship with an SVG implementation, but the docs include an example. My version makes two changes to that example: it drops the &lt;code&gt;xmlns&lt;/code&gt; check (since not all SVGs include it) and adds a check for &lt;code&gt;javascript:&lt;/code&gt; protocol handlers:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SvgXssSecurityAnalyzer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFileStreamSecurityAnalyzer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// reduce memory footprint by partially reading the file&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startBuffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endBuffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadExactly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeekOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SeekOrigin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadExactly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;startBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endString&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;               &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/svg&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsConsideredSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// do not use a using as this will dispose of the underlying stream&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streamReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;script&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/script&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;javascript:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Registration is a single line in DI:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFileStreamSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SvgXssSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Storing SVG Content for Inline Rendering&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;To inline SVGs in your templates, you need the actual SVG markup available at render time. You could read the file on every request, but that&#x27;s expensive. Storing the markup as a property on the media item means it gets served from Umbraco&#x27;s cache instead of hitting IO on every render.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Add a custom property called &lt;code&gt;svgContent&lt;/code&gt; to the &lt;code&gt;Vector Graphics (SVG)&lt;/code&gt; media type in Umbraco, then create a notification handler that populates it automatically whenever an SVG is saved.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The property type matters here. A &lt;code&gt;Label (string)&lt;/code&gt; won&#x27;t work - SVG markup easily exceeds 512 characters. If you&#x27;re on Umbraco 17.3.0&#x2B; the &lt;code&gt;Label (long string)&lt;/code&gt; type stores as &lt;code&gt;Text&lt;/code&gt; in the database, but on earlier 17.x versions there&#x27;s a &lt;a href=&quot;https://github.com/umbraco/Umbraco-CMS/issues/21853&quot; rel=&quot;noopener noreferrer&quot;&gt;known issue&lt;/a&gt; where it&#x27;s capped at 512 characters. If that affects you, you&#x27;ll need a custom data type to store the content.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//Umbraco extension manifest:&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;propertyEditorUi&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Website.PropertyEditorUi.LongLabel&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Website Long label&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;elementName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;umb-property-editor-ui-label&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Long Label&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon-list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;propertyEditorSchemaAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umbraco.Plain.Json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//Means we get IHtmlEncodedString in c#&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Using a &lt;code&gt;MediaSavingNotification&lt;/code&gt; handler to read the file content, clean up the markup, and stores it in &lt;code&gt;svgContent&lt;/code&gt; whenever an SVG is saved. For each SVG node it generates a &lt;code&gt;viewBox&lt;/code&gt; from existing dimensions if one doesn&#x27;t exist, strips &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes, and removes the &lt;code&gt;xmlns&lt;/code&gt; declaration. The &lt;code&gt;viewBox&lt;/code&gt; logic here is based on the approach used in &lt;a href=&quot;https://github.com/umbraco-community/Our-Umbraco-TagHelpers&quot; rel=&quot;noopener noreferrer&quot;&gt;Our.Umbraco.TagHelpers&lt;/a&gt; - well worth a look if you haven&#x27;t already:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;HtmlDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HtmlDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DocumentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewbox&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewbox&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;s&quot;&gt;$&quot;0 0 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;xmlns&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This all happens once at upload time. Editors don&#x27;t need to think about it. The full handler with error handling is in the complete example at the end of the post.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The handler is registered on the Umbraco builder:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddNotificationAsyncHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaSavingNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Cleaning Up the Image Pipeline for SVGs&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Remember that issue with adding width and height to SVG output. here&#x27;s how to fix that&lt;/p&gt;&#xA;&#xA;&lt;p&gt;what about something along this line?&lt;br&gt;&#xA;The &lt;code&gt;DontCropSvgsImageUrlGenerator&lt;/code&gt; wraps the default generator. If the image is an SVG, it creates a new &lt;code&gt;ImageUrlGenerationOptions&lt;/code&gt; with only the URL - stripping all crop and resize parameters. Everything else passes through normally:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageUrlGenerationOptions&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EndsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_imageGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ImageUrlGenerationOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_imageGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The registration replaces Umbraco&#x27;s default &lt;code&gt;IImageUrlGenerator&lt;/code&gt;, wrapping the original &lt;code&gt;ImageSharpImageUrlGenerator&lt;/code&gt; inside the decorator:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoveAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IImageUrlGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//Umbraco only has one by default&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IImageUrlGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DontCropSvgsImageUrlGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ImageSharpImageUrlGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SixLabors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageSharp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequestAuthorizationUtilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetRequiredService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageSharpMiddlewareOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;());&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DontCropSvgsImageUrlGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Rendering the SVG Content&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With all of this in place, using SVGs in Razor views is straightforward. An extension method on &lt;code&gt;MediaWithCrops&lt;/code&gt; surfaces the stored SVG content (using the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#extension-members&quot; rel=&quot;noopener noreferrer&quot;&gt;C# 14 extension members&lt;/a&gt; syntax on .NET 10):&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaWithCrops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHtmlEncodedString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SvgContent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UmbracoMediaVectorGraphics&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SvgContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SvgContent&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Then in a component, render the SVG inline if available, falling back to an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag otherwise:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight html&quot;&gt;&lt;code&gt;@if (Model.Icon.SvgContent is not null)&#xA;{&#xA;    @Model.Icon.SvgContent&#xA;}&#xA;else&#xA;{&#xA;    &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@Model.Icon.GetCropUrl(48, 48)&quot;&lt;/span&gt;&#xA;         &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;@Model.Icon.GetAltText()&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;loading=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lazy&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;An alternative here would be to create a custom tag helper in a similar way to Our.Umbraco.TagHelpers.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Wrapping Up&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Together it gives you a solid SVG story in Umbraco: editors upload SVGs as normal, the code secures them, fixes the sizing, and serves them from a single URL or inline with full CSS control as the developer and site need.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Full Handler&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s the complete &lt;code&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/code&gt; ready to copy-paste:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INotificationAsyncHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaSavingNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SvgContentPropertyAlias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;svgContent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MediaFileManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mediaFileManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;MediaFileManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mediaFileManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StoreSvgContentMediaSavingNotificationHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;_mediaFileManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mediaFileManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HandleAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;MediaSavingNotification&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SavedEntities&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;InvariantEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UmbConstants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conventions&lt;/span&gt;&#xA;                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VectorGraphicsAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SvgContentPropertyAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mediaFileManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StreamReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&#xA;                    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEndAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;                &lt;span class=&quot;n&quot;&gt;HtmlDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HtmlDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DocumentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SelectNodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//svg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;                &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                        &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewbox&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                                &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;                                &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;                            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decimal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                                &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;                                &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;                            &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetAttributeValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewbox&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                                &lt;span class=&quot;s&quot;&gt;$&quot;0 0 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;                        &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                        &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;height&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;                    &lt;span class=&quot;n&quot;&gt;svgNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;xmlns&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;                &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DocumentNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OuterHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SvgContentPropertyAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IOException&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                    &lt;span class=&quot;s&quot;&gt;&quot;Unable to read file to set SVG content &quot;&lt;/span&gt;&#xA;                    &lt;span class=&quot;p&quot;&gt;&#x2B;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;for media item with ID {MediaId}.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                    &lt;span class=&quot;n&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  References&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/security/server-side-file-validation#ifilestreamsecurityanalyzer&quot; rel=&quot;noopener noreferrer&quot;&gt;Server-side file validation | Umbraco Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://github.com/umbraco-community/Our-Umbraco-TagHelpers&quot; rel=&quot;noopener noreferrer&quot;&gt;Our.Umbraco.TagHelpers&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;]]></description>
                    <pubDate>Mon, 16 Mar 2026 09:22:37 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/ocufmmemberslookup-package/</guid>
                    <title>OC.UFMMembersLookup package</title>
                    <link>https://owain.codes/blog/2026/march/ocufmmemberslookup-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/the-agent-ready-cms-introducing-agent-skills-umbraco-backoffice-extension/</guid>
                    <title>The Agent-Ready CMS:  Introducing Agent Skills: Umbraco Backoffice Extension</title>
                    <link>https://umbraco.com/blog/the-agent-ready-cms-introducing-agent-skills-umbraco-backoffice-extension/</link>
                    <description><![CDATA[From turning rough ideas into working wireframes to building complex backoffice extensions without hallucinations, discover how we&#x2019;re making AI a practical tool for Umbraco development.]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.urestore</guid>
                    <title>Umbraco.Community.uRestore</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.urestore</link>
                    <description><![CDATA[Extends the Umbraco backoffice with a Property Restore workspace tab, allowing editors to compare any saved content version against the current draft and selectively restore individual property values without affecting the rest of the content.]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/pulseforumbraco</guid>
                    <title>PulseForUmbraco</title>
                    <link>https://marketplace.umbraco.com/package/pulseforumbraco</link>
                    <description><![CDATA[Your Umbraco site&#x27;s operational health monitor. Pulse scans your content, configuration, performance, and security &#x2014; giving you an actionable health score and one-click remediation from inside the backoffice.]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/ocufmmemberlookup-package/</guid>
                    <title>OC.UFMMemberLookup Package</title>
                    <link>https://owain.codes/blog/2026/march/ocufmmemberlookup-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.ufmmemberlookup</guid>
                    <title>OC.UFMMemberLookup</title>
                    <link>https://marketplace.umbraco.com/package/oc.ufmmemberlookup</link>
                    <description><![CDATA[An Umbraco Flavoured Markdown extension to allow for searching Member data]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://cornehoskam.com/posts/why-implicit-scoring-isnt-always-enough-for-personalization/</guid>
                    <title>Why Implicit Scoring Isn&#x27;t Always Enough for Personalization</title>
                    <link>https://cornehoskam.com/posts/why-implicit-scoring-isnt-always-enough-for-personalization/</link>
                    <description><![CDATA[Implicit scoring works great for most visitors, but what happens when the system gets it wrong? Let&#x27;s take a look at how Explicit Scoring in Umbraco Engage gives marketers the confidence to stop guessing.]]></description>
                    <pubDate>Mon, 16 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.Separator/releases/tag/v17.0.0</guid>
                    <title>Limbo.Umbraco.Separator 17.0.0</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.Separator/releases/tag/v17.0.0</link>
                    <description><![CDATA[Limbo.Separator is a small package for Umbraco that adds a property editor serving as a separator to give a better overview of properties within your Umbraco content types.]]></description>
                    <pubDate>Sun, 15 Mar 2026 20:58:37 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://www.enkelmedia.se/blogg/2026/3/15/programmatically-create-umbraco-members-and-set-password?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</guid>
                    <title>Programmatically create Umbraco Members and set Password</title>
                    <link>https://www.enkelmedia.se/blogg/2026/3/15/programmatically-create-umbraco-members-and-set-password?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
                    <description><![CDATA[&lt;p&gt;Over the last months, I&#x27;ve been working on moving websites from Umbraco 13 up to Umbraco 17. I had a really hard time to find good documentation on how to work with Members so I figured I&#x27;ll write a short blog post to fill some of the &quot;gaps&quot; I experienced in the documentation.&lt;/p&gt;&#xA;&lt;h2&gt;What is Member in Umbraco?&lt;/h2&gt;&#xA;&lt;p&gt;A &lt;strong&gt;Member&lt;/strong&gt; in Umbraco is an external or frontend user. In other words, this is the kind of account you would use for things like a customer area, intranet login, gated content, forums, and similar functionality on the public-facing site.&lt;/p&gt;&#xA;&lt;h2&gt;Working with Members programmatically&lt;/h2&gt;&#xA;&lt;p&gt;When working with Umbraco Members programmatically you need to familiarize your self with the &lt;em&gt;IMemberService &lt;/em&gt;and the &lt;em&gt;IMemberManager&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;IMemberService is used to work with the IMember object, set username, email and update custom properties.&lt;/li&gt;&#xA;&lt;li&gt;IMemberManager is used to perform login, get current member and set password.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Here is a code sample that will create a member programmatically and set the password programmatically.&lt;/p&gt;&#xA;&lt;div&gt;&#xD;&#xA;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class MemberRegistrationService&#xA;{&#xA;    private readonly IMemberService _memberService;&#xA;    private readonly IMemberManager _memberManager;&#xA;    private readonly IPasswordHasher _passwordHasher;&#xA;&#xA;    public MemberRegistrationService(&#xA;        IMemberService memberService,&#xA;        IMemberManager memberManager,&#xA;        IPasswordHasher passwordHasher&#xA;        )&#xA;    {&#xA;        _memberService = memberService;&#xA;        _memberManager = memberManager;&#xA;        _passwordHasher = passwordHasher;&#xA;    }&#xA;&#xA;    public async Task&amp;lt;IMember&amp;gt; CreateMemberAsync(string email, string password, string name)&#xA;    {&#xA;        var member = _memberService.CreateMember(&#xA;            email,&#xA;            email,&#xA;            name,&#xA;            &amp;quot;Member&amp;quot;); // or Member.ModelTypeAlias with ModelsBuilder&#xA;        &#xA;        member.IsApproved = true;&#xA;&#xA;        _memberService.Save(member);&#xA;&#xA;        // Optionally add to a role&#xA;        _memberService.AssignRole(member.Id, &amp;quot;Member&amp;quot;); &#xA;&#xA;        // Set password&#xA;        var memberIdentity = await _memberManager.FindByIdAsync(member.Id.ToString());&#xA;        var passwordResult = await _memberManager.AddPasswordAsync(memberIdentity!, password);&#xA;&#xA;        // Alternative way of setting password&#xA;        member.RawPasswordValue = _passwordHasher.HashPassword(password);&#xA;&#xA;        return member;&#xA;    }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xD;&#xA;&lt;/div&gt;&#xD;&#xA;&#xD;&#xA;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;&#xA;&lt;p&gt;..&lt;/p&gt;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;]]></description>
                    <pubDate>Sun, 15 Mar 2026 14:19:20 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/d_inventor/im-learning-python-with-ai-and-tdd-40aa</guid>
                    <title>I&#x27;m learning python with AI and TDD</title>
                    <link>https://dev.to/d_inventor/im-learning-python-with-ai-and-tdd-40aa</link>
                    <description><![CDATA[&lt;p&gt;I decided to make my next hobby project in python. I used this project to learn python, but also to become familiar with AI-assisted coding. In this article I document my experiences with AI as a pair programming buddy, doing Test-Driven Development together.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  About TDD, the project and me&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Test-Driven Development is about writing tests before writing your code. This is often done in the Red, Green, Refactor cycle: Write a failing test, specifying the behaviour that the code should exhibit, make all tests pass with as few changes as possible and finally make your code (and your tests) nice. It&#x27;s a design tool that helps make your code easier to understand and maintain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The project is a self-hosted information database that connects &lt;a href=&quot;https://animalcrossing.nintendo.com/amiibo/#welcome&quot; rel=&quot;noopener noreferrer&quot;&gt;Animal Crossing Amiibos&lt;/a&gt; to information on &lt;a href=&quot;https://nookipedia.com/wiki/Main_Page&quot; rel=&quot;noopener noreferrer&quot;&gt;Nookipedia&lt;/a&gt;. An API exposes the data so that an app can scan an amiibo and direct the user straight to the character&#x27;s page on the Nookipedia. The project, besides providing desired functionality, is also an excuse for me to try out some enterprise software patterns that I wish to learn more about.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&quot;ltag-github-readme-tag&quot;&gt;&#xA;  &lt;div class=&quot;readme-overview&quot;&gt;&#xA;    &lt;h2&gt;&#xA;      &lt;img src=&quot;https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg&quot; alt=&quot;GitHub logo&quot;&gt;&#xA;      &lt;a href=&quot;https://github.com/D-Inventor&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        D-Inventor&#xA;      &lt;/a&gt; / &lt;a href=&quot;https://github.com/D-Inventor/my-animal-crossing-app&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        my-animal-crossing-app&#xA;      &lt;/a&gt;&#xA;    &lt;/h2&gt;&#xA;    &lt;h3&gt;&#xA;      This is a hobby project. It is a combination of an app for android and a python-based server API. The server supplies information about Animal Crossing villagers. The app lets me scan Amiibo cards and then I can see basic information about the villager.&#xA;    &lt;/h3&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&quot;ltag-github-body&quot;&gt;&#xA;    &#xA;&lt;div id=&quot;readme&quot; class=&quot;md&quot;&gt;&#xA;&lt;p&gt;This is a hobby project.&#xA;I wanted to get easy access to basic villager information from my Amiibo card collection.&lt;/p&gt;&#xA;&lt;p&gt;This project combines an android app with a python-based API so that I can scan the NFC chip of an amiibo card and then fetch the information. That&#x27;s all.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;/div&gt;&#xA;&lt;br&gt;&#xA;  &lt;div class=&quot;gh-btn-container&quot;&gt;&lt;a class=&quot;gh-btn&quot; href=&quot;https://github.com/D-Inventor/my-animal-crossing-app&quot; rel=&quot;noopener noreferrer&quot;&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;&#xA;&lt;br&gt;&#xA;&lt;/div&gt;&#xA;&lt;br&gt;&#xA;&#xA;&#xA;&lt;p&gt;I am a dotnet developer with very little python experience. Python has a special place in my heart, because it&#x27;s the first language that I earned some money with. That was more than 10 years ago and I haven&#x27;t touched python that much since then.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  AI as a pair programming buddy&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Most of the code in this project has been written by AI. Overall, the results were mostly good, sometimes very frustrating, but sometimes also very impressive.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Scaffolding a project baseline&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Yes, AI got me started very quickly and effectively. I knew what I wanted: multiple python projects in a monorepo with some shared modules. I know what python code looks like, but actually setting up a project like this is quite a lot. Setting up testing with pytest, multiple independent modules with a shared pyproject.toml. Module imports are very confusing in python and if you don&#x27;t know what you&#x27;re doing, they&#x27;re difficult to get right sometimes. I asked the AI to do it for me with working examples of unit tests and shared modules. It did all of it, the code ran, the tests worked and the code looked acceptable. Big win &#x1F4AA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  So many comments, so much &quot;documentation&quot;&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;It was really my own fault and it was quickly fixed. The AI was writing excessive comments. Every generated function filled with comments and &quot;documentation&quot;, detailing exactly what the code does. That&#x27;s not what I need comments for. I can read the code to see what it does. The thing here is that I let the AI generate its own instruction file and the instruction file literally instructed the AI to do this. I simply updated the instruction file and instructed the AI to never write comments.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Stop! Wait! Not so fast! Not so much!&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Some people may find it wonderful: just tell the AI what you want to make and it&#x27;ll happily build the whole thing for you. Or try to at least... Although it&#x27;s magnificent, I think it&#x27;s also incredibly irresponsible to let AI generate large amounts of code at once. Especially in a professional environment, I would be held personally responsible for bugs and mistakes and saying &quot;it&#x27;s the fault of the AI&quot; simply will not fly. It&#x27;s essential that I can review the code that the AI generates and I can only do this if I develop in small steps.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Test-Driven Development has been very helpful to put a leash on the AI. AI is very eager to write code and will simply sprint to the finish line if you let it. By strictly following the Red, Green, Refactor cycle, I was able to stay more in control of what the AI does and how it does it. It took a bit of trial-and-error, but the following instructions seemed to be very helpful:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Follow strict TDD practice with Red, Green and Refactor phases&lt;/li&gt;&#xA;&lt;li&gt;Always run the test in the Red phase to verify that the test fails in the way that we expect&lt;/li&gt;&#xA;&lt;li&gt;Prefer runtime failures over compile-time failures at all times&lt;/li&gt;&#xA;&lt;li&gt;Prefer failures on assertions over failures in code under test whenever this is reasonable&lt;/li&gt;&#xA;&lt;li&gt;Make sure in the green phase to write as little code as possible to make the test pass&lt;/li&gt;&#xA;&lt;li&gt;Always ask for verification from the user after each phase before proceeding to the next&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Additionally, it seemed helpful when I start each session with &quot;We are pair programming buddies and we are going to do Test Driven Development&quot;. Nevertheless, the AI would still occasionally do more than I asked for, especially the green phase was difficult at times.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Please give me an example in the form of an automated test&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;In this project I needed a handful of slightly more advanced concepts, including:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Messaging&lt;/li&gt;&#xA;&lt;li&gt;HTTP API calls&lt;/li&gt;&#xA;&lt;li&gt;Database querying&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Though the concepts are familiar, I had never done them in python specifically. I know approximately what good integrations look like, but I simply needed some examples in python specifically. AI has been an excellent tool for learning and it helped me to understand python frameworks, tools and syntax. By asking about it in the shape of an automated test, I could also immediately apply the concepts I learned with Test Driven Development. This was a massive win! I would like to share a particular experience that really blew my mind.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I needed to build an integration with an external HTTP API. Coincidentally, I recently also had to do this for work in dotnet, so I had a good idea what a good integration looks like and also how a good unit test looks for such an integration. It had taken me about 5 hours to build in dotnet. I asked the AI to generate me an example of a unit test that demonstrates an http request and response in python. the unit test was quite bloated and unreadable, but it did work. I told the AI as much and I gave it an example of what I expected the test to look like, based on a test that I wrote in dotnet. I asked to refactor the test to make it as close as possible to the example that I gave. In just a few minutes it generated exactly the testing utility that I had spent several hours on earlier! This was shocking and incredibly impressive.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Thoughts overall&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In general, I find the workflow decent. Although I did get much further with AI in a shorter amount of time than I would have without, I don&#x27;t believe (yet) that AI is a massive time-saver overall. That sounds like a contradiction, but you must see that it was my lack of understanding of the python ecosystem that held me back and not the speed by which I type code. Although the AI can create code much faster than me, the code is often not up to standards and at times I have had to throw away work of the past 10 minutes, because prompting for corrections was only making things worse.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;An important thing, in my opinion, is that AI may replace your typing, but shouldn&#x27;t replace your brain. The skills that I&#x27;ve developed as a professional developer are still required to judge the quality of the output and are still essential to prioritize long-term success over short-term gains.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So yeah, if you haven&#x27;t tried it yet, perhaps you could also turn your AI into your TDD pair programming buddy. Thank you for reading and maybe I&#x27;ll see you in my next blog! &#x1F60A;&lt;/p&gt;]]></description>
                    <pubDate>Sun, 15 Mar 2026 08:57:27 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.accessibilitytoolkit</guid>
                    <title>DigitalWonderlab.AccessibilityToolkit</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.accessibilitytoolkit</link>
                    <description><![CDATA[Comprehensive WCAG 2.1 accessibility auditing for Umbraco backoffice. 37 checks across Level A, AA, and AAA with scored results, scan history, site-wide audits, printable reports, and CSV export. Lightweight with no external dependencies &#x2014; works on Umbraco Cloud and all hosting environments out of the box.]]></description>
                    <pubDate>Sat, 14 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/uprofile-march-2026-debasish-gracias/</guid>
                    <title>uProfile March 2026 - Debasish Gracias</title>
                    <link>https://umbraco.com/blog/uprofile-march-2026-debasish-gracias/</link>
                    <description><![CDATA[On a mission to inspire, connect, and bring together the global Umbraco community]]></description>
                    <pubDate>Fri, 13 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.uaccessible</guid>
                    <title>Umbraco.Community.uAccessible</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.uaccessible</link>
                    <description><![CDATA[Accessibility audit reports for your Umbraco content pages, directly in the backoffice.]]></description>
                    <pubDate>Fri, 13 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://dev.to/jemayn/a-first-look-at-load-balancing-the-umbraco-backoffice-1ihm</guid>
                    <title>A first look at load balancing the Umbraco backoffice</title>
                    <link>https://dev.to/jemayn/a-first-look-at-load-balancing-the-umbraco-backoffice-1ihm</link>
                    <description><![CDATA[&lt;p&gt;When Umbraco 17 released there was a lot of focus on it being the first LTS version of Umbraco using the new web component based backoffice, and for a lot of people that was important enough in itself.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I must admit the only other feature I paid attention to is that dates now consistently have a timezone on them making it much easier to work with them.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, one thing I somehow managed to miss completely was that as of Umbraco 17 you can now load balance the backoffice of Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Isn&#x27;t load balancing Umbraco an old feature?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Load balancing Umbraco sites has been around for longer than I have used Umbraco - so at least since v7!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It used to work a certain way as shown in this diagram based on the official documentation:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkahi3hzslz6mjahtzlen.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkahi3hzslz6mjahtzlen.png&quot; alt=&quot;Traditional Umbraco load balancing architecture&quot; width=&quot;502&quot; height=&quot;719&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing#how-umbraco-load-balancing-works&quot; rel=&quot;noopener noreferrer&quot;&gt;Source: Umbraco documentation&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Which basically meant that what most people doing load balancing would do is spin up 2 Azure Web Apps, deploy the same code to both of them but have some settings that would designate one web app as the frontend app and one as the backend app.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Both apps would use the same database, and with some specific settings added you could allow the frontend app to scale out - for example by setting some rules that determines what amount of load the frontend should be under before it starts additional instances to automatically scale out.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This works pretty well, but also means that the minimal amount of infrastructure was 2 web apps - and if you had a lot of editors then there was no way to scale out the backend to accommodate extra load there.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Load balancing the backoffice&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;So the new thing in Umbraco 17 is that you can now load balance everything. One immediate effect that could have is that you now just need a single web app for a basic scale out site, something like this:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o0plpamgcszwjnc54pn.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o0plpamgcszwjnc54pn.png&quot; alt=&quot;New Umbraco load balancing architecture&quot; width=&quot;742&quot; height=&quot;615&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&quot;crayons-card c-embed&quot;&gt;&#xA;&#xA;  &lt;br&gt;&#xA;Shout out &lt;a href=&quot;https://mermaid.js.org/&quot; rel=&quot;noopener noreferrer&quot;&gt;MermaidJS charts&lt;/a&gt;. AI agents are very good at translating described flows into charts, and it makes it so much easier to create visuals!&lt;br&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;p&gt;This setup could theoretically make it cheaper, and it&#x27;s also easier to have a regular site running just 1 instance with the option of scaling out for big peaks in load without a bunch of extra setup! &lt;/p&gt;&#xA;&#xA;&lt;p&gt;So first you have to decide between the &quot;old&quot; way of load balancing with separate backend and frontend webapps and the &quot;new&quot; way of having a single scaleable app for both.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Secondly if choosing the &quot;new&quot; way of scaling a single app there is also a choice of whether the backoffice should use sticky sessions or be stateless.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There are a lot of articles about pros and cons for both:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.linode.com/docs/guides/configuring-load-balancer-sticky-session/&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.linode.com/docs/guides/configuring-load-balancer-sticky-session/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-best-practices#check-your-distribution-mode&quot; rel=&quot;noopener noreferrer&quot;&gt;https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-best-practices#check-your-distribution-mode&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But in short the difference is how traffic is distributed:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Sticky sessions&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Once a request hits the load balancer it is assigned to an instance and a cookie is returned on the response that has a reference to that instance. &lt;/li&gt;&#xA;&lt;li&gt;Future requests pass the cookie along to the load balancer, and it ensures that the new request hits the same instance.&lt;/li&gt;&#xA;&lt;li&gt;If x amount of users cause the server resources to spike to the point that more instances are added based on the load balancing rules, you can end up having a lot of users &quot;stick&quot; to the initial instance due to their cookies.&lt;/li&gt;&#xA;&lt;li&gt;If the instance a user is &quot;stuck&quot; to gets removed during a scale-down, the load balancer assigns them to a new instance - which can cause problems (more on that below).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Stateless&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Once a request hits the load balancer it is assigned to the instance with the least load&lt;/li&gt;&#xA;&lt;li&gt;This ensures a much more equal distribution between instances&lt;/li&gt;&#xA;&lt;li&gt;This also makes things like sessions or SignalR connections a lot more complicated to manage&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Setting up a backoffice load balanced site&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The following setup applies to both sticky sessions and stateless, with the exception of SignalR which differs between the two and is covered separately at the end.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The assumption here is that the load balanced site will be hosted on Azure as a web app. &lt;br&gt;&#xA;The current documentation will always be the most relevant resource: &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice&quot; rel=&quot;noopener noreferrer&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As of writing this for Umbraco 17, there are some settings that should be set no matter if it is a sticky session or stateless setup:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Set &lt;code&gt;Umbraco:Cms:Examine:LuceneDirectoryFactory&lt;/code&gt; to &lt;code&gt;TempFileSystemDirectoryFactory&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Set &lt;code&gt;Umbraco:Cms:Hosting:LocalTempStorageLocation&lt;/code&gt; to &lt;code&gt;EnvironmentTemp&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Set the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice#server-role-accessor&quot; rel=&quot;noopener noreferrer&quot;&gt;Server role accessor&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice#load-balancing-repository-caches&quot; rel=&quot;noopener noreferrer&quot;&gt;IsolatedCaches&lt;/a&gt;:&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoadBalancingComposer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IComposer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;    &#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IUmbracoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;        &#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LoadBalanceIsolatedCaches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetServerRegistrar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StaticServerAccessor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StaticServerAccessor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IServerRoleAccessor&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServerRole&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrentServerRole&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ServerRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SchedulingPublisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Note: In the traditional setup you&#x27;d have one SchedulingPublisher and multiple Subscribers. With the new backoffice load balancing, check the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice#server-role-accessor&quot; rel=&quot;noopener noreferrer&quot;&gt;current documentation&lt;/a&gt; for how server roles should be assigned, as this may differ from what you&#x27;re used to.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Session management&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you use sticky sessions then it may seem like nothing is needed for managing sessions as you &quot;stick&quot; to one instance and that instance will handle your session just like on a non-loadbalanced site.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, if you consider this scenario:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;You run a webshop with automatic scaling set up &amp;amp; sticky sessions&lt;/li&gt;&#xA;&lt;li&gt;You have a big sale, and the site scales up from 1 to 4 instances&lt;/li&gt;&#xA;&lt;li&gt;A user is assigned to the 4th instance and starts adding things to their basket (which is stored as a session)&lt;/li&gt;&#xA;&lt;li&gt;While that user is browsing traffic lowers, and the automatic scaling scales down to 3 instances&lt;/li&gt;&#xA;&lt;li&gt;The user tries to add a new item to their basket - but the API controller request now hits the load balancer with a sticky session cookie to an instance that has been removed. &lt;/li&gt;&#xA;&lt;li&gt;The load balancer assigns them to a new instance, which has no knowledge of their previous session. Their basket now only contains the item they just added - everything else is gone.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;The same thing would happen for auth cookies as they use &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/introduction?view=aspnetcore-10.0&quot; rel=&quot;noopener noreferrer&quot;&gt;Data Protection&lt;/a&gt; - by default each instance generates its own encryption keys, meaning cookies encrypted on one instance can&#x27;t be decrypted by another. So similarly to the example above, users may need to log in again each time they are moved to a new instance.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Dependent on your site this may not be a big problem, but if you use anything with sessions / auth cookies then it is very bad user experience.&lt;/p&gt;&#xA;&lt;h4&gt;&#xA;  &#xA;  &#xA;  Setting up out of process sessions and data protection&#xA;&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;The solution to both the session and auth cookie problem is to move them out of the individual instances and into a shared store.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is needed regardless of whether you use sticky sessions or stateless load balancing:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;With &lt;strong&gt;sticky sessions&lt;/strong&gt;, users are normally pinned to one instance - but as shown in the scenario above, scaling events can force them onto a different instance. Without shared sessions and data protection, that means lost session data and forced re-authentication.&lt;/li&gt;&#xA;&lt;li&gt;With &lt;strong&gt;stateless&lt;/strong&gt; load balancing, every single request can land on a different instance, making shared sessions and data protection essential for even basic functionality.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;In this example we&#x27;ll use Redis, but any &lt;code&gt;IDistributedCache&lt;/code&gt; implementation would work.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You&#x27;ll need the following NuGet packages:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.Extensions.Caching.StackExchangeRedis&quot; rel=&quot;noopener noreferrer&quot;&gt;Microsoft.Extensions.Caching.StackExchangeRedis&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Microsoft.AspNetCore.DataProtection.StackExchangeRedis&quot; rel=&quot;noopener noreferrer&quot;&gt;Microsoft.AspNetCore.DataProtection.StackExchangeRedis&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;There are three things we need to set up:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;1. A shared distributed cache&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddStackExchangeRedisCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redisConnectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstanceName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my-site-&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This registers Redis as the &lt;code&gt;IDistributedCache&lt;/code&gt; implementation. By default, ASP.NET Core uses an in-memory implementation which is exactly what causes the problem - each instance has its own cache. By pointing this at a shared Redis instance, all app instances share the same cache.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;InstanceName&lt;/code&gt; is used as a key prefix in Redis, which is useful if you share a Redis instance across multiple applications.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As a bonus, Umbraco&#x27;s content and media cache is built on Microsoft&#x27;s &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/configuration/cache-settings&quot; rel=&quot;noopener noreferrer&quot;&gt;HybridCache&lt;/a&gt; - which automatically uses any registered &lt;code&gt;IDistributedCache&lt;/code&gt; as a second-level cache. So by registering Redis here, Umbraco will also use it to share its content and media cache across instances.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;2. Session configuration&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IdleTimeout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromMinutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpOnly&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsEssential&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurePolicy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CookieSecurePolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Always&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;ASP.NET Core sessions use whatever &lt;code&gt;IDistributedCache&lt;/code&gt; is registered - so because we registered Redis above, sessions are now automatically stored in Redis instead of in-memory.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The cookie settings are worth noting:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;HttpOnly&lt;/code&gt; prevents JavaScript from accessing the session cookie&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;IsEssential&lt;/code&gt; ensures the cookie is set even if the user hasn&#x27;t consented to non-essential cookies&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;SecurePolicy = Always&lt;/code&gt; ensures the cookie is only sent over HTTPS&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;3. Shared Data Protection keys&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redisConnection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redisConnectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddDataProtection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetApplicationName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-umbraco-site&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;PersistKeysToStackExchangeRedis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redisConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DataProtection-Keys&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This is what solves the auth cookie problem. By default, each instance generates its own Data Protection keys - meaning a cookie encrypted on instance 1 can&#x27;t be decrypted by instance 2.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;By persisting the keys to Redis and setting a shared &lt;code&gt;ApplicationName&lt;/code&gt;, all instances use the same encryption keys. The &lt;code&gt;ApplicationName&lt;/code&gt; is important - instances must share the same application name to be able to decrypt each other&#x27;s cookies.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  SignalR&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;This is the final piece of the puzzle, and the only one where the setup actually differs depending on whether you&#x27;re using sticky sessions or stateless.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco uses &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/signalr-in-backoffice-load-balanced-environment&quot; rel=&quot;noopener noreferrer&quot;&gt;SignalR&lt;/a&gt; in the backoffice for real-time client-to-server communication outside of standard HTTP requests - for example for preview functionality. In a load balanced setup, SignalR needs a way to send messages across all instances, not just the one the user is connected to. This is called a backplane.&lt;/p&gt;&#xA;&#xA;&lt;h4&gt;&#xA;  &#xA;  &#xA;  Sticky sessions&#xA;&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;With sticky sessions, client-side SignalR connections work as normal - the user has a persistent connection to one instance, just like on a single-server site. The only thing needed is a backplane so that server-side messages (like &quot;this content was just published&quot;) can reach all instances.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Redis works well for this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Requires: Microsoft.AspNetCore.SignalR.StackExchangeRedis&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSignalR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddStackExchangeRedis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redisConnectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h4&gt;&#xA;  &#xA;  &#xA;  Stateless&#xA;&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;With stateless load balancing, each request can land on a different instance. SignalR relies on a persistent connection between client and server - and that connection can&#x27;t just &quot;jump&quot; to a new instance on every request. So a Redis backplane alone isn&#x27;t enough here.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The solution is to use a managed SignalR service like Azure SignalR Service, which handles both the backplane and the client-side connections externally:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Requires: Microsoft.Azure.SignalR&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddSignalR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAzureSignalR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;azureSignalRConnectionString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This replaces the need for the Redis backplane entirely for SignalR - Azure SignalR Service handles everything. Note that you still need Redis for the distributed cache, sessions, and data protection as set up above.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Wrapping up&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The stateless approach does give a smoother distribution of load across instances, but Azure SignalR Service adds a significant cost on top of the Redis you already need for everything else.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In most cases I would go with the sticky session setup - the lower cost and the simplicity of having everything managed through a single Redis instance makes it easy to set up and maintain. And once sessions and data protection are out of process, sticky sessions handle instance swaps gracefully anyway.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That said, I haven&#x27;t done any load testing to compare the two approaches. If they scale differently under real-world traffic, that could easily be the biggest factor in choosing one over the other. This post is really just meant to highlight the differences and walk through the setup - not to declare a winner. Try both if you can, and pick what fits your situation.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 12 Mar 2026 21:06:17 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/luukpeters/upgrade-umbraco-13-to-17-property-editors-property-value-converters-22kb</guid>
                    <title>Upgrade Umbraco 13 to 17: Property Editors &#x2B; Property Value Converters</title>
                    <link>https://dev.to/luukpeters/upgrade-umbraco-13-to-17-property-editors-property-value-converters-22kb</link>
                    <description><![CDATA[&lt;p&gt;This is part five in a series about common tasks you&#x27;ll encounter when upgrading Umbraco 13 to 17. In this part, we&#x2019;ll look at updating Property Editors and Property Value Converters.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When upgrading several packages, one area that caused confusion was Property Editors. In Umbraco 17 there is a much clearer separation between the UI in the backoffice and the data handling and validation on the backend (C#).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Because of this change, Umbraco introduced migrations that convert existing Data Types (which are essentially instances of a Property Editor) to the new format. In Umbraco 13 a Data Type only referenced a single editor alias, but in Umbraco 17 it contains two: an &lt;strong&gt;editor alias&lt;/strong&gt; (backend) and an &lt;strong&gt;editor UI alias&lt;/strong&gt; (frontend). This makes it possible, for example, to use multiple interchangeable UIs that work with the same underlying data.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why won&#x27;t my property editor work anymore?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;After upgrading my database to Umbraco 17 I ran into a couple of issues:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How does Umbraco know that it should use my newly created Property Editor UI for existing properties?&lt;/li&gt;&#xA;&lt;li&gt;Why does Models Builder suddenly return a &lt;code&gt;JsonDocument&lt;/code&gt; instead of my custom &lt;code&gt;VideoPlayerValueConverterModel&lt;/code&gt;?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The answer lies in understanding what the migration from Umbraco 13 to 14&#x2B; actually does and what you need to do with the result. This post walks through that process.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This blog explains how you can update your Property Editors to work in Umbraco 17 without having to create a custom migration. It also helps to read the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/property-editors/composition&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco documentation on Property Editor composition&lt;/a&gt;. That documentation explains the different pieces that make up a Property Editor in Umbraco 17 and provides useful context for what follows.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The starting point before the migration&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There are two different starting points for Property Editors in Umbraco 13, and each leads to a slightly different outcome during the migration to Umbraco 14&#x2B;. The difference depends on whether the Property Editor is defined in a &lt;strong&gt;package.manifest&lt;/strong&gt; file or in &lt;strong&gt;C# code&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Starting point 1: manifest-only Property Editor&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;In Umbraco 13 a Property Editor can be defined purely in a &lt;code&gt;package.manifest&lt;/code&gt;, for example:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;propertyEditors&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;alias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Video player&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;icon&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;icon-play&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Proud Nerds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;view&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~/App_Plugins/ProudNerds.Umbraco.VideoPlayer/umbraco.videoplayer.html&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This is enough to create a simple data editor. The value is stored as a string in the database and there is no server-side validation. Anything can be stored as long as it fits into a string.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If needed, you can still transform that value later using a Property Value Converter before it ends up in the cache.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Starting point 2: Property Editor defined in code&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;You can also &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/13.latest/tutorials/creating-a-property-editor#setting-up-a-property-editor-with-csharp&quot; rel=&quot;noopener noreferrer&quot;&gt;define a Property Editor in C#&lt;/a&gt;, for example:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Core.PropertyEditors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;YourProjectName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Suggestions editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Suggestions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/App_Plugins/Suggestions/suggestion.html&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Common&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;icon-list&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Suggestions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataEditor&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IDataValueEditorFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataValueEditorFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataValueEditorFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;            &#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Defining a Property Editor in code gives you more control over validation and how the data is stored.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The resulting database records&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When you create a Data Type in Umbraco 13 that uses a Property Editor, the resulting database record is almost identical for both approaches. Each Data Type simply stores a property editor alias and configuration in the &lt;code&gt;umbracoDataType&lt;/code&gt; table.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuicwlvzwi8diylzqvd6h.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuicwlvzwi8diylzqvd6h.png&quot; alt=&quot; &quot; width=&quot;631&quot; height=&quot;167&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you register the editor in C#, the &lt;code&gt;dbType&lt;/code&gt; may differ, but that detail is not important for this discussion.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The data migration (13 &#x2192; 14&#x2B;)&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The difference between the two approaches becomes visible during migration.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In one of the later Umbraco 13 versions a &lt;strong&gt;pre-migration&lt;/strong&gt; was introduced that prepares Property Editors for upgrading to Umbraco 14&#x2B;. During this step, a record is written to the &lt;code&gt;umbracoKeyValue&lt;/code&gt; table describing how each Data Type should be migrated.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For a manifest-only Property Editor, the record looks like this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;DataTypeId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1055&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;EditorUiAlias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;EditorAlias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Umbraco.Plain.String&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;However, if the editor was defined in C#, both aliases will be the same:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;DataTypeId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1055&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;EditorUiAlias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;EditorAlias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;After the database upgrade to Umbraco 17, this information is used to populate the new &lt;code&gt;propertyEditorAlias&lt;/code&gt; and &lt;code&gt;propertyEditorUiAlias&lt;/code&gt; columns in the &lt;code&gt;umbracoDataType&lt;/code&gt; table.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For the video player editor, the result of the migration looks like this:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58t3d0gffuy81n6oww8m.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58t3d0gffuy81n6oww8m.png&quot; alt=&quot; &quot; width=&quot;772&quot; height=&quot;148&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Updating the code&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Once you understand what the migration produced in the database, updating the code becomes much clearer.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Creating and registering the Property Editor UI&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;With the new backoffice introduced in Umbraco 14&#x2B;, the UI part of your Property Editor needs to be recreated because AngularJS is no longer supported.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;After creating the UI, you need to register it. I won&#x27;t go into the details of building a Property Editor UI here (the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/property-editors/composition/property-editor-ui&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation&lt;/a&gt; covers that), but the aliases you use during registration are important.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Based on the migration result:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;alias&lt;/code&gt; should match &lt;strong&gt;PropertyEditorUiAlias&lt;/strong&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;code&gt;propertyEditorSchemaAlias&lt;/code&gt; should match &lt;strong&gt;PropertyEditorAlias&lt;/strong&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;For example:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;propertyEditorUi&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;proudnerds.videoplayer.editor&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Proud Nerds video player property editor&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./proud-nerds-video-property-editor-ui.element&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Video player&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon-play&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Proud Nerds&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;propertyEditorSchemaAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umbraco.Plain.Json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Updating the DataEditor (if it already exists)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If your editor already had a &lt;code&gt;DataEditor&lt;/code&gt; implementation, you can continue using it, but it needs to be updated for Umbraco 17.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Most of the implementation remains similar to Umbraco 13. The main difference is that several parameters have been removed from the &lt;code&gt;DataEditor&lt;/code&gt; attribute because of the clearer separation between UI and backend logic.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Umbraco 13&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Video Player&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/App_Plugins/VideoPlayer/videoplayer.html&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Proud Nerds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Icon&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;icon-play&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VideoPlayerEditor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataEditor&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// Umbraco 17&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DataEditor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VideoPlayerEditor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DataEditor&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Updating the Property Value Converter (if needed)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you used the manifest-only approach in Umbraco 13, there is a good chance your Property Value Converter no longer works.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;During migration, the &lt;code&gt;EditorAlias&lt;/code&gt; for the video editor changed from:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;proudnerds.videoplayer.editor&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;to:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;Umbraco.Plain.Json&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;So if your Property Value Converter checks the editor alias, it will no longer match:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPublishedPropertyType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The simplest fix is to check the &lt;strong&gt;EditorUiAlias&lt;/strong&gt; instead:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsConverter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPublishedPropertyType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;propertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EditorUiAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;proudnerds.videoplayer.editor&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This is not entirely semantically correct, but it is often the easiest solution without introducing additional migrations. After this, your Property Editor will work as expected again and the models builder will use your custom model instead of the generic JsonDocument.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Done&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Once you understand how the Property Editor migration works, it becomes much easier to determine which aliases to use in &lt;code&gt;umbraco-package.json&lt;/code&gt; and what code changes are required after upgrading.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The concepts themselves are straightforward, but the migration can be confusing the first time you encounter it. Hopefully this overview helps clarify what is happening behind the scenes.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  EditorUiAlias or not?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Earlier I mentioned that checking &lt;code&gt;EditorUiAlias&lt;/code&gt; inside the &lt;code&gt;IsConverter&lt;/code&gt; method of a Property Value Converter is not entirely correct from a semantic point of view.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A Property Value Converter operates on the data stored in the database and converts that to something else to put in the cache. A Property Editor UI is only the interface used to edit that data. In fact, multiple UIs could theoretically exist for the same underlying editor.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Because of that, checking &lt;code&gt;EditorUiAlias&lt;/code&gt; introduces some risk. If a different UI were introduced for the same editor, your converter might no longer match the correct data. Checking &lt;code&gt;EditorAlias&lt;/code&gt; is the safest way to guarantee you are handling the expected data structure.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That said, in many real-world scenarios a Property Editor has exactly one UI and one DataEditor that always belong together. If you fully control the implementation, checking &lt;code&gt;EditorUiAlias&lt;/code&gt; can be a pragmatic and perfectly workable solution that avoids writing additional migrations.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Opinions on this tend to differ. Some developers strongly prefer to always check &lt;code&gt;EditorAlias&lt;/code&gt; for correctness, while others consider &lt;code&gt;EditorUiAlias&lt;/code&gt; acceptable when the editor and UI are tightly coupled. In the end, the choice depends on how strictly you want to follow the separation between UI and data.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 12 Mar 2026 13:30:20 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=_RUYI1xNzdE</guid>
                    <title>DevRel Deep Dive: Exploring Umbraco.AI with Matt Brailsford</title>
                    <link>https://www.youtube.com/watch?v=_RUYI1xNzdE</link>
                    <description><![CDATA[This week Sebastiaan and Lotte are joined by Matt Brailsford, Staff Engineer at Umbraco, to show Umbraco.AI in action. Umbraco.AI is Umbraco&#x2019;s free and open source AI foundation and integration add-on for the CMS. It&#x2019;s provider-agnostic, with NuGet packages for OpenAI, Anthropic and more. They also look at the other two main packages that Umbraco HQ has developed: Umbraco.AI.Prompt and Umbraco.AI.Agent. Watch, learn, and then try them for yourself!&#xA;&#xA;&#x1F4D6; Matt&#x2019;s &quot;kitchen sink&quot; getting started blog post: https://mattbrailsford.dev/umbraco-ai-kitchen-sink-install&#xA;&#x1F440; Umbraco.AI source code on GitHub: https://github.com/umbraco/Umbraco.AI&#xA;&#xA;--------&#xA;&#x2753; Looking for help with your Umbraco projects? Visit the Forum&#xA; https://forum.umbraco.com&#xA; &#xA;&#x1F4AC; Want to chat with the friendly Umbraco Community? Join our Discord server&#xA; https://discord.umbraco.com&#xA; &#xA;&#x2709;&#xFE0F; Got a question (or topic suggestion) for the HQ Developer Relations team?&#xA; https://umbra.co/contact-devrel, or the &#x2018;contact-devrel&#x2019; channel on Discord&#xA; &#xA;#Umbraco]]></description>
                    <pubDate>Thu, 12 Mar 2026 10:24:56 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://medium.com/@glenstark883/boost-your-website-with-expert-umbraco-development-services-3ca0c166f387?source=rss------umbraco-5</guid>
                    <title>Boost Your Website with Expert Umbraco Development Services</title>
                    <link>https://medium.com/@glenstark883/boost-your-website-with-expert-umbraco-development-services-3ca0c166f387?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@glenstark883/boost-your-website-with-expert-umbraco-development-services-3ca0c166f387?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*zGapSohko4R6HMLCG_1Vqw.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;In today&amp;#x2019;s competitive digital landscape, businesses need websites that are fast, scalable, and easy to manage.&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@glenstark883/boost-your-website-with-expert-umbraco-development-services-3ca0c166f387?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Thu, 12 Mar 2026 07:55:23 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=1vCk_13FGsQ</guid>
                    <title>AI in Umbraco: Feature demo</title>
                    <link>https://www.youtube.com/watch?v=1vCk_13FGsQ</link>
                    <description><![CDATA[AI in Umbraco: &#xA;Bolette Kern (CMS Product Manager) and Matt Brailsford (Staff Engineer) provide a deep dive into the rapid evolution of AI in Umbraco following the 2026 Winter Keynote. This update showcases how AI has transitioned from a nested setting to a core, dedicated section of the Umbraco back office, offering better organization and granular permission control for developers and content editors.&#xA;&#xA;The session covers significant functional upgrades to AI prompts and agents, including the ability to generate multiple content variations and the introduction of &quot;Auto Agents&quot; that intelligently select the best tool for your specific request. You will also get an exclusive first look at the upcoming Evaluation and Testing dashboard, designed to help organizations benchmark AI performance, accuracy, and cost.&#xA;&#xA;This video is for Umbraco developers, architects, and content strategists looking to leverage AI within their CMS workflows. Whether you are building custom AI integrations or using HQ packages, these updates provide the foundation for secure, scalable, and context-aware AI implementations.&#xA;&#xA;Video Chapters:&#xA;00:00 Introduction with Bolette Kern and Matt Brailsford&#xA;00:55 The New Dedicated AI Back Office Section&#xA;02:14 Enhanced Prompts: Generating Multiple Options&#xA;03:25 AI Agents vs. Prompts: Key Differences&#xA;04:05 Security: Permissions, Scopes, and User Group Overrides&#xA;05:30 Context Awareness and Location-Based Agents&#xA;06:35 The Auto Agent: Intelligent Prompt Routing&#xA;07:58 Preview: The AI Testing and Evaluation Dashboard&#xA;12:56 Developer Updates: Deploy, Notifications, and Field Validation&#xA;14:37 Future Outlook and Community Hackathons&#xA;&#xA;Key Takeaways:&#xA;- Dedicated AI Workspace: AI now resides in its own back office section, improving discoverability and allowing for specific user permissions independent of the settings section.&#xA;&#xA;- Iterative Content Generation: Prompts can now return up to five different options simultaneously, allowing editors to compare and select the best result.&#xA;&#xA;- Granular Security: The new Scopes system allows administrators to group AI tools and manage access at the user group level for fine-grained control.&#xA;&#xA;- Intelligent Routing: The Auto Agent feature analyzes user intent to automatically trigger the most relevant specialist, such as a Legal Assistant or a Content Creator.&#xA;&#xA;- Quality Assurance: The upcoming Testing section allows for &quot;mock data&quot; evaluations and &quot;graders&quot; to ensure AI responses remain consistent as models evolve.&#xA;&#xA;Resources and Links: &#xA;Umbraco AI Product Page: https://umbraco.com/ai/&#xA;&#xA;Umbraco Winter Keynote (Reference): https://youtu.be/GK-eGhzZYpw&#xA;&#xA;Latest Release Blog Post: https://umbraco.com/blog/the-agent-ready-cms-what-are-agent-skills/&#xA;&#xA;Umbraco Marketplace: https://marketplace.umbraco.com/package/umbraco.ai&#xA;&#xA;Developer Events and Hackathons: https://www.meetup.com/pro/umbraco/]]></description>
                    <pubDate>Thu, 12 Mar 2026 07:16:58 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/modgift.umbraco.imageupload</guid>
                    <title>Modgift.Umbraco.ImageUpload</title>
                    <link>https://marketplace.umbraco.com/package/modgift.umbraco.imageupload</link>
                    <description><![CDATA[Automatically resizes and converts images uploaded to Umbraco CMS 17 media library.]]></description>
                    <pubDate>Thu, 12 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-product-update-q1-2026/</guid>
                    <title>Umbraco Product Update - Q1 2026</title>
                    <link>https://umbraco.com/blog/umbraco-product-update-q1-2026/</link>
                    <description><![CDATA[Explore this update featuring the launch of Umbraco Compose, a new AI strategy, enhanced security in Umbraco Cloud, and a new era for Search.]]></description>
                    <pubDate>Thu, 12 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.uschema</guid>
                    <title>Umbraco.Community.uSchema</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.uschema</link>
                    <description><![CDATA[Validates JSON-LD structured data on published Umbraco content pages via a workspace tab in the backoffice.]]></description>
                    <pubDate>Tue, 10 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/march/auto-updating-your-github-readme-with-your-latest-blog-posts/</guid>
                    <title>Auto-Updating Your GitHub README with Your Latest Blog Posts</title>
                    <link>https://owain.codes/blog/2026/march/auto-updating-your-github-readme-with-your-latest-blog-posts/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 10 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-cms-security-advisory-march-10-2026-security-patches-are-now-available/</guid>
                    <title>Umbraco CMS Security Advisory, March 10, 2026</title>
                    <link>https://umbraco.com/blog/umbraco-cms-security-advisory-march-10-2026-security-patches-are-now-available/</link>
                    <description><![CDATA[Security patches are available for Umbraco versions 16 and 17 to fix three moderate-to-high vulnerabilities. Upgrade now to secure your CMS. Projects on Umbraco Cloud will automatically receive the fixes.]]></description>
                    <pubDate>Tue, 10 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/backofficethemes</guid>
                    <title>BackofficeThemes</title>
                    <link>https://marketplace.umbraco.com/package/backofficethemes</link>
                    <description><![CDATA[Extra themes for the Umbraco backoffice &#x2014; including Christmas (theme plus snow, lights, tree, Santa hat) and popular editor-style themes: Neon, Cyberpunk, Dracula, Monokai, VS Blue, VS Cool Breeze, and VS Icy Mint.]]></description>
                    <pubDate>Mon, 09 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116182281314587750</guid>
                    <title>One week left to submit your Umbraco Awards 2026 entry!</title>
                    <link>https://umbracocommunity.social/@umbraco/116182281314587750</link>
                    <description><![CDATA[One week left to submit your Umbraco Awards 2026 entry! &#x1F570;&#xFE0F; No video needed - just the facts. Partners can turn entries into https://umbraco.com case studies for extra exposure. Streamlined form, minimal effort. Deadline: March 13. Submit: https://umbra.co/4l7zW42 #Umbraco]]></description>
                    <pubDate>Fri, 06 Mar 2026 15:32:53 Z</pubDate>
                        <category>social</category>
                        <category>mastodon</category>
                        <category>hq</category>
                </item>
                <item>
                    <guid>https://discord.com/channels/869656431308189746/882981290662580264/1479418096111583373</guid>
                    <title>ValidationAttributes package now under the Umbraco Community GitHub account</title>
                    <link>https://discord.com/channels/869656431308189746/882981290662580264/1479418096111583373</link>
                    <description><![CDATA[With blessings from the original author we have taken the ValidationAttributes package for Umbraco and forked it into a repo under the Umbraco Community GitHub account.&#xA;&#xA;I have released a stable for V17 that can be download now!&#xA;&#xA;https://marketplace.umbraco.com/package/umbraco.community.validationattributes&#xA;&#xA;#umbraco #community]]></description>
                    <pubDate>Fri, 06 Mar 2026 09:59:31 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>discord</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/116176456661920079</guid>
                    <title>The Umbraco Spark 2026 programme is now live</title>
                    <link>https://umbracocommunity.social/@umbracospark/116176456661920079</link>
                    <description><![CDATA[The Umbraco Spark 2026 programme is now live &#x1F389;Take a look at all the talks and timings for the day and start planning your day!It&#x2019;s going to be a great one &#x2728;&#x1F449; https://umbracospark.com/ #Umbraco #UmbracoSpark #WebCommunity]]></description>
                    <pubDate>Thu, 05 Mar 2026 14:53:09 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.nathanielnunes.com/blog/securing-umbraco-images-with-hmac</guid>
                    <title>Securing Umbraco Images with HMAC</title>
                    <link>https://www.nathanielnunes.com/blog/securing-umbraco-images-with-hmac</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 05 Mar 2026 08:45:58 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/indexing-media-values-for-documents/</guid>
                    <title>Indexing media values for documents</title>
                    <link>https://kjac.dev/posts/indexing-media-values-for-documents/</link>
                    <description><![CDATA[Leveraging the Umbraco Search extension model to index media ALT texts for their referenced documents.]]></description>
                    <pubDate>Thu, 05 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/how-to-audit-umbraco-content-for-ai-references-aeo-geo-readiness/</guid>
                    <title>How to Audit Umbraco Content for AI References (AEO / GEO Readiness)</title>
                    <link>https://umbraco.com/blog/how-to-audit-umbraco-content-for-ai-references-aeo-geo-readiness/</link>
                    <description><![CDATA[A practical Umbraco guide to AEO/GEO: how to structure content so AI systems can find, extract, and reference the right sections.]]></description>
                    <pubDate>Thu, 05 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.validationattributes</guid>
                    <title>Umbraco.Community.ValidationAttributes</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.validationattributes</link>
                    <description><![CDATA[[Umbraco v17&#x2B;] Contains validation attributes to decorate your classes, but using Umbraco Dictionary as the resource.]]></description>
                    <pubDate>Thu, 05 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/116170806922855214</guid>
                    <title>Join Candid Contributions LIVE at Umbraco Spark</title>
                    <link>https://umbracocommunity.social/@umbracospark/116170806922855214</link>
                    <description><![CDATA[&#x1F399;&#xFE0F; LIVE at Umbraco Spark!During lunch time, join us for a live session with Candid Contributions featuring special guests and open, honest conversations from the community.Grab your lunch, bring your thoughts and join the discussion.Details &#x1F449; https://umbracospark.com/ #UmbracoSpark #OpenSource #CandidContributions]]></description>
                    <pubDate>Wed, 04 Mar 2026 12:04:31 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/umbraco-ai-kitchen-sink-install</guid>
                    <title>The Umbraco AI Kitchen Sink Install</title>
                    <link>https://mattbrailsford.dev/umbraco-ai-kitchen-sink-install</link>
                    <description><![CDATA[Since we released Umbraco AI, one of the most common bits of feedback I&#x2019;ve heard is: &#x201C;I want to try it, but I&#x2019;m not sure which packages to...]]></description>
                    <pubDate>Wed, 04 Mar 2026 11:19:26 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/af.umbraco.redis.connector</guid>
                    <title>AF.Umbraco.Redis.Connector</title>
                    <link>https://marketplace.umbraco.com/package/af.umbraco.redis.connector</link>
                    <description><![CDATA[Redis connector for Umbraco 15/16/17 (.NET 9/10). Auto-composes StackExchange.Redis distributed cache, persists ASP.NET Core Data Protection keys to Redis (default key: AF.Umbraco.Redis.Connector:_DataProtectionKeys), performs startup fail-fast connectivity validation, and includes optional smoke endpoints.]]></description>
                    <pubDate>Wed, 04 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/umbracian-bristol-guide/</guid>
                    <title>The Umbracian&#x27;s Guide to Bristol (2026)</title>
                    <link>https://joe.gl/ombek/blog/umbracian-bristol-guide/</link>
                    <description><![CDATA[Are you heading to Umbraco Spark and hoping to explore Bristol while you&#x27;re here? I live near Bristol and now consider myself a Spark veteran, so thought I&#x27;d share my insights!]]></description>
                    <pubDate>Tue, 03 Mar 2026 09:44:22 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#125</guid>
                    <title>Skrift Issue #125</title>
                    <link>https://skrift.io/issues/#125</link>
                    <description><![CDATA[Featuring guest posts by Nathaniel Grantham-Knight on &quot;Building An Umbraco Property Editor With Vite&quot; and Tim Payne on &quot;Web Development and Ultrarunning Have More in Common Than You Might Think&quot;]]></description>
                    <pubDate>Tue, 03 Mar 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/the-agent-ready-cms-what-are-agent-skills/</guid>
                    <title>The Agent-Ready CMS: What are Agent Skills?</title>
                    <link>https://umbraco.com/blog/the-agent-ready-cms-what-are-agent-skills/</link>
                    <description><![CDATA[Discover how Agent Skills and MCP are transforming Umbraco into an Agent-Ready CMS. Learn to power intelligent actions and future-proof your agency&#x27;s workflow.]]></description>
                    <pubDate>Tue, 03 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116164694359696472</guid>
                    <title>Umbraco MVP nominations deadline near</title>
                    <link>https://umbracocommunity.social/@umbraco/116164694359696472</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 03 Mar 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.3</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.3</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.3</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Mon, 02 Mar 2026 23:13:30 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116159361781744592</guid>
                    <title>Umbraco Codegarden 2026 program is live</title>
                    <link>https://umbracocommunity.social/@umbraco/116159361781744592</link>
                    <description><![CDATA[&#x201C;Should we just&#x2026; put the program live?&#x201D;&#x201C;Yes.&#x201D;&#x201C;Like&#x2026; now?&#x201D;&#x201C;Yes.&#x201D;&#x201C;Should our CEO hold a sign?&#x201D;&#x201C;Obviously.&#x201D;So here we are.Codegarden 2026 program = LIVE &#x1FAA7;&#x1F499;Two full days.45&#x2B; sessions.60&#x2B; speakers.3 podcasts.4 workshops. Tech and business - all in one place.We&#x2019;ll be there. Will you?Explore the full program and secure your spot &#x1F447;https://codegarden.umbraco.com/program/ #Umbraco]]></description>
                    <pubDate>Mon, 02 Mar 2026 13:07:15 Z</pubDate>
                        <category>mastodon</category>
                        <category>social</category>
                        <category>hq</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/koola.parentdropdown</guid>
                    <title>Koola.ParentDropDown</title>
                    <link>https://marketplace.umbraco.com/package/koola.parentdropdown</link>
                    <description><![CDATA[Umbraco 17 property editor that resolves options from a parent-defined root picker and supports checkbox, radio, and dropdown selection modes.]]></description>
                    <pubDate>Sat, 28 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/act/</guid>
                    <title>Running GitHub Actions .NET and Azure workflows locally with Act</title>
                    <link>https://joe.gl/ombek/blog/act/</link>
                    <description><![CDATA[Act is a fantastic tool for testing GitHub actions locally instead of pushing that 10th commit in a row called Testing GitHub Actions (again) but configuring it to work correctly can be a balancing act, so here are some tips for getting Act working with your .NET CI/CD flows.]]></description>
                    <pubDate>Fri, 27 Feb 2026 11:29:14 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://jcdc.dev/blog/introducing-new-package-documentation</guid>
                    <title>Introducing docs.jcdc.dev</title>
                    <link>https://jcdc.dev/blog/introducing-new-package-documentation</link>
                    <description><![CDATA[Explore the new documentation website for all jcdcdev Umbraco packages. Built with Astro and Starlight for high performance and low carbon impact, featuring automated synchronisation across GitHub and the Umbraco Content Delivery API.]]></description>
                    <pubDate>Fri, 27 Feb 2026 11:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-workspaces-l63</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 -Workspaces</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-workspaces-l63</link>
                    <description><![CDATA[&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;All the code for this series of posts is available in the &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco Repository on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;In the last article we got to the point where we had our own custom section, with a side menu (or &quot;sidebarApp&quot;) and a basic menu item. &lt;br&gt;&#xA;but for now clicking on that menu item just shows us some loading dots &#x1F61E;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiupotg4rkjyddxm8vv07.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiupotg4rkjyddxm8vv07.png&quot; alt=&quot;Empty section&quot; width=&quot;800&quot; height=&quot;524&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;What we need to do know is define a workspace. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;So what are workspaces ? well to lift the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/workspaces&quot; rel=&quot;noopener noreferrer&quot;&gt;description directly from the umbraco docs&lt;/a&gt; : &lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Workspaces provide dedicated editing environments for specific entity types in Umbraco. They create isolated areas where users can edit content, media, members, or other entities with specialized interfaces and functionality.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;basically menu items are assigned an &#x27;entity type&#x27; and when you click on them Umbraco looks for the workspace that renders that entity type. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;entity types are really just Identifiers at this point, usually you might have in two entity types a &#x27;root&#x27; and and &#x27;item&#x27; - the root is as you might expect the one at the root, and the item is an item in your tree (if you have one.&lt;/p&gt;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Item workspace&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For our &quot;simple&quot; time item, we only have a single &quot;do-stuff-time-item&quot; entity type, and that&#x27;s the one we need to build a workspace for to show information to the user. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Workspaces at their most basic have two things, a workspace context and a workspace element. &lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Workspace Context&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Your workspace context is the code that controls the state and data for your workspace. here you have all your data and ways to fetch and save it. &lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;To be clear, you don&#x27;t need to do this in a context, you can just fetch , store and manipulate data in the element, but as you will see in a bit, that is probably going to lead to duplication and a more complex bunch of code&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;So without being to complicated a workspace will look something like this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MySampleWorkspaceContext&lt;/span&gt; &#xA;  &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UmbEditableWorkspaceContextBase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TimeSettings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&#xA;  &lt;span class=&quot;k&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbSubmittableWorkspaceContext&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;settingsRepository&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoStuffTimeSettingsRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;  &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;workspaceAlias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;nf&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbControllerHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provideContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_CONTEXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;nf&quot;&gt;getUnique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;nf&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;nf&quot;&gt;getEntityType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_TIME_ITEM_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;settingsRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;saveTimeSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Workspaces can even be simpler than this, but here we are implementing a &lt;code&gt;UmbSubmittableWorkspaceContext&lt;/code&gt; because we want to be able to save things that we update on our context, if you don&#x27;t want to tie into other things like workspace actions, you don&#x27;t need to do this, but its a relatively simple implementation, so in my opinion it&#x27;s worth it. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;There are few things we are going to pick out in the context : &lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  ProvideContext&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;You can see that inside the constructor for the context we are calling &lt;code&gt;this.provideContext&lt;/code&gt; . this tells the rest of the app, that we are responsible for the &lt;code&gt;DOSTUFF_WORKSPACE_CONTEXT&lt;/code&gt; so if anything wants to interact with this context they can. by asking for it in their own constructor. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;For example - a view might ask for the context like this.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;consumeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_CONTEXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;c1&quot;&gt;// do stuff with the context here.&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Workspace contexts have a scope, they will only exist while you are in the workspace, you can&#x27;t call a workspace context from somewhere else or another section - if you need to do that you might need a global context, which work in a similar way they are just defined slightly differently&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Repositories&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Another thing you might have noticed about this context is we randomly just call save in a &lt;code&gt;#settingsRepository&lt;/code&gt; and we&#x27;ve haven&#x27;t told you what one of them is yet - we will get to them in a bit. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;But for now, that&#x27;s just the place where the actual calls to API end points and the like are stored - it means our context doesn&#x27;t need to know the inner workings of the API it can just say go get this or save that.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Views&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;So now we have registered our workspace and we have a workspace context, you will notice, we still have nothing to show to our user! So now its time to fix that, and present them with something. You define what things show in a workspace via &lt;code&gt;workspaceView&lt;/code&gt; elements. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;again we define these in a manifest.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;   &lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;workspaceView&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.DefaultWorkspaceView&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Default Workspace View&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./default-workspace-view.element.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;nl&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#doStuff_defaultWorkspaceViewName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;nx&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon-alarm-clock&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umb.Condition.WorkspaceAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;So the things to note : &lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;the &lt;code&gt;js&lt;/code&gt; entry points the file that will render the element&lt;/li&gt;&#xA;&lt;li&gt;the &lt;code&gt;meta&lt;/code&gt; data defines the name and icon for the view&lt;/li&gt;&#xA;&lt;li&gt;the &lt;code&gt;conditions&lt;/code&gt; make sure our view only shows up on our workspace.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  View element&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The view element is a &lt;code&gt;UmbLitElement&lt;/code&gt; that renders what you want to show the user&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;customElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;do-stuff-default-workspace-view-element&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoStuffDefaultWorkspaceViewElement&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UmbLitElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;nx&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&amp;lt;umb-body-layout&amp;gt;&#xA;      &amp;lt;div class=&quot;layout&quot;&amp;gt;&#xA;        &amp;lt;uui-box&#xA;          .headline=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;doStuff_defaultWorkspaceTitle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#xA;        &amp;gt;&#xA;          &amp;lt;h1&amp;gt;Hello Time&amp;lt;/h1&amp;gt;&#xA;        &amp;lt;/uui-box&amp;gt;&#xA;      &amp;lt;/div&amp;gt;&#xA;    &amp;lt;/umb-body-layout&amp;gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;With this in place we get something to show the user ! &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqp9kqy3c3n5xulhq78jc.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqp9kqy3c3n5xulhq78jc.png&quot; alt=&quot;Single workspace view&quot; width=&quot;800&quot; height=&quot;547&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Multiple views, and icons.&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;So we defined that icon, and name, but it&#x27;s not anywhere on the screen, what&#x27;s up with that?.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Well the icon and name are used when we have multiple views in a workspace. so if we add another &lt;code&gt;workspaceView&lt;/code&gt; manifest to the workspace&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;nl&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;workspaceView&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.SettingsWorkspaceView&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Settings Workspace View&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./settings-workspace-view.element.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;nl&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#doStuff_settingsWorkspaceViewName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon-settings&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umb.Condition.WorkspaceAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_WORKSPACE_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;We get our &#x27;tabs&#x27;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnwn8csctdp2dsckkj7b.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnwn8csctdp2dsckkj7b.png&quot; alt=&quot;Tabs&quot; width=&quot;800&quot; height=&quot;166&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Summarry&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This gets us the skeleton of our workspace up and showing something to the user - there is much more to workspaces , and we will go into them in some later posts, but for now, we have a section, a menu and some workspaces. so &#x1F389;&lt;/p&gt;]]></description>
                    <pubDate>Fri, 27 Feb 2026 09:50:36 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/february/how-i-used-umbracoai-for-free/</guid>
                    <title>How I used Umbraco.AI for free</title>
                    <link>https://owain.codes/blog/2026/february/how-i-used-umbracoai-for-free/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 27 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/how-i-used-umbracoai-for-free/</guid>
                    <title>How I used Umbraco.AI for free</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/how-i-used-umbracoai-for-free/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 27 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/d_inventor/umbraco-testing-examples-now-also-for-umbraco-17-3d5k</guid>
                    <title>Umbraco testing examples now also for Umbraco 17</title>
                    <link>https://dev.to/d_inventor/umbraco-testing-examples-now-also-for-umbraco-17-3d5k</link>
                    <description><![CDATA[&lt;p&gt;I updated my testing examples repository to the latest Umbraco version.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&quot;ltag-github-readme-tag&quot;&gt;&#xA;  &lt;div class=&quot;readme-overview&quot;&gt;&#xA;    &lt;h2&gt;&#xA;      &lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg&quot; alt=&quot;GitHub logo&quot;&gt;&#xA;      &lt;a href=&quot;https://github.com/D-Inventor&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        D-Inventor&#xA;      &lt;/a&gt; / &lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        automated-testing-in-umbraco&#xA;      &lt;/a&gt;&#xA;    &lt;/h2&gt;&#xA;    &lt;h3&gt;&#xA;      A working example of integration- and unittests with Umbraco. A demonstration of various concepts for testing your Umbraco website&#xA;    &lt;/h3&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&quot;ltag-github-body&quot;&gt;&#xA;    &#xA;&lt;div id=&quot;readme&quot; class=&quot;md&quot;&gt;&#xA;&lt;div class=&quot;markdown-heading&quot;&gt;&#xA;&lt;h1 class=&quot;heading-element&quot;&gt;Umbraco 17 automated testing setup&lt;/h1&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;This project is a fully functioning setup for automated testing with Umbraco 17. You can use this project as a reference or starting point to get started with testing on your Umbraco website. The tests are set up with Test Driven Development (TDD) in mind.&lt;/p&gt;&#xA;&lt;div class=&quot;markdown-heading&quot;&gt;&#xA;&lt;h2 class=&quot;heading-element&quot;&gt;Tools&lt;/h2&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The most important tools that are used in the automated tests are as follows:&lt;/p&gt;&#xA;&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;Name&lt;/th&gt;&#xA;&lt;th&gt;Description&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://xunit.net/?tabs=cs&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;xUnit v3&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;The testing framework. You can use any testing framework that you like though&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://nsubstitute.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NSubstitute&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;Library for mocking. Any mocking library will work. This project doesn&#x27;t do extensive mocking, but for example &lt;code&gt;IPublishedValueFallback&lt;/code&gt; is a mandatory parameter for any published content item, even if you don&#x27;t actually use it. It&#x27;s just convenient to insert a mock.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://testcontainers.com/&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Test Containers&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;Automatically creates docker containers while running tests. It is used to create an empty SQL Server database that is automatically cleaned up after testing.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#x2026;&lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&quot;gh-btn-container&quot;&gt;&lt;a class=&quot;gh-btn&quot; href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco&quot; rel=&quot;noopener noreferrer&quot;&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;p&gt;Though not much has changed, here are the most notable differences compared to the Umbraco 16 version&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Database initialization&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;SqlServerDatabase&lt;/code&gt; resource no longer creates a second database inside the SqlServer test container. While Umbraco 16 would happily create a new database on boot if it didn&#x27;t exist yet, Umbraco 17 does not and will throw connection errors.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For your regular use of Umbraco, this change makes no significant difference. If you also use EF Core, you may need to pay some extra attention here!&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A base URL for integration tests&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;WebsiteFixture&lt;/code&gt; will now set a fixed base URL when creating a backoffice httpclient. If you need to use a different domain, this base URL can be adapted, but what is important is that the URL uses &lt;strong&gt;https&lt;/strong&gt; protocol. Compared to Umbraco 16, Umbraco 17 requires https to backoffice endpoints by default and requests using http are rejected by default. That&#x27;s at least what the error would indicate.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you want to see all the changes, you can check out this commit:&lt;br&gt;&#xA;&lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco/commit/573ef96a155c390ba3d4383bf555f9ff5723b3b4&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/D-Inventor/automated-testing-in-umbraco/commit/573ef96a155c390ba3d4383bf555f9ff5723b3b4&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  An additional note&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I have given some attention to the example website under test in this repository. The homepage at least has a slightly more fancy template, but it&#x27;s all static content. I haven&#x27;t yet taken the time to really tie it all together, and I wanted to do so using Test Driven Development, which is the main focus of this example repository.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I noticed that the version of playwright for C# doesn&#x27;t actually let you compare screenshots!? This was a mild surprise and disappointment, because that means I can&#x27;t actually use snapshot testing to ensure my template doesn&#x27;t accidentally change when I implement the logic. The Javascript version of playwright does seem to support it, so I guess for better end-to-end testing, we&#x27;d need to create an equivalent management API client and scenario builder. Oof!&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  That&#x27;s all&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;that&#x27;s all I wanted to share! Hope you check out the repository, give it a star and let me know if the testing examples have been helpful to you or not! Thank you for reading &#x1F60A;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:48:29 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-sections-5ha2</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Sections</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-sections-5ha2</link>
                    <description><![CDATA[&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;So now, we are getting into the code! as with all the bits in this series all the code is available on the &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/blob/v17/main&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco repo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  1. Sections&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;(see &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main/src/DoStuff.Client/DoStuff-Client/src/section&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbracoRepo : Sections&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A section in umbraco is something accessed from the top bar navigation menu. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2suol9gjit4yqc3g7hqo.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2suol9gjit4yqc3g7hqo.png&quot; alt=&quot;Umbraco top navigation&quot; width=&quot;800&quot; height=&quot;55&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Its quite simple to add a new section - all you need is the manifest.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sectionManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_SECTION_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Section&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#doStuff_sectionName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;do-stuff&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Constants&lt;/strong&gt;&lt;br&gt;&#xA;&lt;em&gt;Here we have used a constant for the alias, because its often true you will need to reference the section alias in other places on your site.&lt;/em&gt; &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;The alias is defined in another file.&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_SECTION_ALIAS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Section&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&#xA;&lt;p&gt;&lt;em&gt;and this replaces the string value in the manifest.&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Localization&lt;/strong&gt;&lt;br&gt;&#xA;&lt;em&gt;You might also notice that the &lt;code&gt;label&lt;/code&gt; value starts with a &#x27;#&#x27;, this tells umbraco that we want to use a localize value for the label. we will cover them a bit later, but for now just to so you know you can just put a string in for the label value if you want&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  2. Dashboard&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;(see &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main/src/DoStuff.Client/DoStuff-Client/src/dashboard&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco repo : Dashboards&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;At this point your section is quite empty, but you can add a dashboard quite easily to get some content in there. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Dashboards are pages that sit at the top level of a section, almost all the sections in umbraco already have dashboards (Content has news, and redirect, settings has, examine, health checks and profiling to name a few).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;you can add your own dashboard to your own sections or existing sections as you choose.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dashboardManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./dashboard.element.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#DoStuff_DashboardName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;do-stuff-dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umb.Condition.SectionAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_SECTION_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Here we define the alias and element we want to use for our dashboard, and the &lt;code&gt;condition&lt;/code&gt; determines where it lives.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;there are all sorts of conditions in umbraco, but here we are saying if the section alias is that of our custom section then we are happy. &lt;strong&gt;If we didn&#x27;t have a condition the dashboard would appear in all sections&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Again note the use of a constant for the section alias&lt;/em&gt;_&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Dashboards are WebComponents, they are probibly the simplest ones in umbraco you don&#x27;t have to inherit anything (if you don&#x27;t want to) - there are no contexts, or stores or tree&#x27;s required, you element can just render some HTML.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;customElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;do-stuff-dashboard-element&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DoStuffDashboardElement&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UmbLitElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;nx&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&amp;lt;umb-body-layout&#xA;      .headline=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;term&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;doStuff_dashboardTitle&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#xA;    &amp;gt;&#xA;      &amp;lt;uui-box&amp;gt;&#xA;        &amp;lt;umb-localize key=&quot;doStuff_dashboardIntro&quot;&amp;gt;&amp;lt;/umb-localize&amp;gt;&#xA;      &amp;lt;/uui-box&amp;gt;&#xA;    &amp;lt;/umb-body-layout&amp;gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DoStuffDashboardElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Note&lt;/em&gt; : We do inherit from UmbLitElement as opposed to LitElement, this gives you access to the localization helpers amongst other things, so it&#x27;s worth doing, even though you don&#x27;t have to.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljvyc4cfr40ef26i2uip.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljvyc4cfr40ef26i2uip.png&quot; alt=&quot;DoStuff Dashboard.&quot; width=&quot;680&quot; height=&quot;274&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  3. Sidebar App.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You have probibly notices that almost all existing sections in umbraco have some form of menu or tree down the left hand side. In Umbraco speak this is a &quot;SidebarApp&quot; and it can contain almost anything! - but usually its menu items and trees. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;A sidebar is registered in a manifest file.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sidebarManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sectionSidebarApp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.SectionSidebarApp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Section Sidebar App&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#doStuff_sidebarStaticAppName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Static.Menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Umb.Condition.SectionAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_SECTION_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;On its own a sidebar app is quite empty. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfe8xed7udau3menu0uo.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfe8xed7udau3menu0uo.png&quot; alt=&quot;Empty sidebar&quot; width=&quot;800&quot; height=&quot;562&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So we need to add some menus and items.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  4. Menus&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Within a standard sidebar you can have a number of &#x27;menus&#x27; these are the sections you see in the settings section, &quot;Structure&quot;, &quot;Templating&quot; and &quot;Advanced&quot; are Menus&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfm92ljoqovw88oqqnsq.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhfm92ljoqovw88oqqnsq.png&quot; alt=&quot;Umbraco settings section&quot; width=&quot;800&quot; height=&quot;562&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;to add your own, we have a manifest.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;menuManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Static.Menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Static Menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;but with out any menu items you won&#x27;t see much , so lets add a menu item.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timeItemManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;menuItem&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.TimeItem&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff Time Item&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;na&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#doStuff_timeItemName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;icon-time&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;entityType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DOSTUFF_TIME_ITEM_ALIAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;menus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Static.Menu&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You can see how we assign the menu item to the menu, and the menu is actually assigned in the section definition. &lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Don&#x27;t worry to much about the &lt;code&gt;entityType&lt;/code&gt; just yet, its going to get funky when we talk about workspaces. but for now lets just bask in the menu glory.&lt;/em&gt; &lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freh0g9n1v5h16lxr3yoi.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freh0g9n1v5h16lxr3yoi.png&quot; alt=&quot;Custom section, dashboard, and menu item&quot; width=&quot;800&quot; height=&quot;562&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;All the client code in this article is available in the &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main/src/DoStuff.Client/DoStuff-Client/src&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco Client repo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:21:17 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-entry-points-2ke4</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Entry Points</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-entry-points-2ke4</link>
                    <description><![CDATA[&lt;p&gt;So now that bundles have replaced &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-types/backoffice-entry-point&quot; rel=&quot;noopener noreferrer&quot;&gt;entry points&lt;/a&gt; as the place to integrate your Umbraco extension, do we still need entry points?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Yes, i most cases you will still need an entry point, to initialize things like the authentication for your client to talk to the server. or if you are feeling really mischievous to unload other entries from the umbraco registry ! &lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Register your entry point via a manifest.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;all the extension template will do this one for you, but to show the process&lt;/em&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Do Stuff Client Entrypoint&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DoStuff.Client.Entrypoint&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;backofficeEntryPoint&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./entrypoint.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;this is then imported and registered via your &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/blob/v17/main/src/DoStuff.Client/DoStuff-Client/src/bundle.manifests.ts&quot; rel=&quot;noopener noreferrer&quot;&gt;bundle.manifest.ts&lt;/a&gt; file. &lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Auth.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The examples that you get with the templates contain most of the code you will need here for authentication.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt; &lt;span class=&quot;nx&quot;&gt;_host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;consumeContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UMB_AUTH_CONTEXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Get the token info from Umbraco&lt;/span&gt;&#xA;    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getOpenApiConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;baseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;na&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;credentials&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;same-origin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;As we&#x27;ve said in another post we like to add a bit - which keeps the token fresh for each request.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;// client interceptor will get the latest token for the auth&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// context for a request, so if the token has been refreshed&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// since we first got it, we&#x27;ll still have a valid token.&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;interceptors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getLatestToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;      &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`Bearer &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;em&gt;honesty - i am not 100% sure this extra bit is in fact needed - it&#x27;s something we might revisit.&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Unregistering things!&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;So your &lt;code&gt;onInit&lt;/code&gt; method runs when your extension is loaded, and you can if you want start to manipulate Umbraco&#x27;s register, you could for example remove the welcome dashboard this way.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;extensionRegistry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unregister&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Umb.Dashboard.UmbracoNews&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Be carefull! removing things from the register might well make bits of the umbraco backoffice unstable. &lt;/p&gt;&#xA;&lt;/blockquote&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:21:02 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-bundles-100g</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Bundles</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-bundles-100g</link>
                    <description><![CDATA[&lt;p&gt;With the Early adopters guide we talked about Entry points as the start of your front end code journey, but now with a few tweaks since v14 bundles are where it&#x27;s at. &lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What&#x27;s a bundle.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;A &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-types/bundle&quot; rel=&quot;noopener noreferrer&quot;&gt;bundle&lt;/a&gt; is an extension point in Umbraco&#x27;s Backoffice that lets you load JavaScript files and register manifests inside Umbraco&#x27;s system so your code is loaded as part of the Backoffice. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Put simply it&#x27;s the loader of your extension.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;typically your entry point javascript file will only import manifest from around your project and return them for umbraco to ingest.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;entrypoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sectionManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dashboardManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localizationManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;menuManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;workspaceManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;editorManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;modalManifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;As your project grows so will this list, to keep it simple and organised i would recommend that at each level you have manifest that might also import from child folders, before finally having everything brought into the bundle file. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;For example in the &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main/src/DoStuff.Client/DoStuff-Client&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco&lt;/a&gt; repository. we might have a /workspaces/views/manfiest.ts file, that is then imported into the /workspace/manifest.ts file which is the one we import into our bundle file.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&#x2B;-- src&#xA;  - bundle.manifest.ts&#xA;  &#x2B;-- workspaces&#xA;    - manifest.ts&#xA;    &#x2B;-- views&#xA;      - manifest.ts  &#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This means everything needed for the workspace is in /workspace/manifest.ts and we can if need be move it about knowing it&#x27;s all there. It also means any one manifest definition file doesn&#x27;t contain two much information.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  What about manifests in umbraco-package.json&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;There is &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-registry/register-extensions#umbraco-package.json&quot; rel=&quot;noopener noreferrer&quot;&gt;quite a bit in the umbraco docs&lt;/a&gt; about how you can define your manifests inside the &lt;code&gt;umbraco-package.json&lt;/code&gt; file, and you can, but once you go beyond something very very basic (e.g. not just adding localization). you will want to do it via a bundle. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Adding all the manifests via the bundle gives you better typescript &amp;amp; checking support in your project, its neater and it catches the errors quicker. &lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Adding via the umbraco-package.json also requires that the site be restarted for the packages to be loaded (these files are read in by the Backoffice code on start-up). using a bundle as the entry point you can add new things build the scripts and it all appears. &lt;/p&gt;&#xA;&lt;/blockquote&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:20:52 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-setup-2998</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Setup</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-setup-2998</link>
                    <description><![CDATA[&lt;p&gt;In our previous post we&#x27;ve gotten our basic structure for our umbraco extension, but before we dive into code. there are a couple things i like to tweak.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;You can choose to ignore this post!, these are my personal preferences for some of the more obscure bits of the setup, they are not required but they can make things a bit easier later on.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  PackageManifestReader vs umbraco-package.json&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  umbraco-package.json&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When you create a new umbraco extension you will get a &lt;code&gt;umbraco-package.json&lt;/code&gt; file created in your &lt;code&gt;client/public&lt;/code&gt; folder. &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/umbraco-package&quot; rel=&quot;noopener noreferrer&quot;&gt;this file defines the entry point for Umbraco to fetch and initialize your package&lt;/a&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MySuperPackage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MySuperPackage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allowTelemetry&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;My Super Package Bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;alias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MySuperPackage.Bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/MySuperPackage/my-super-package.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;and this works fine - but it has one or two issues. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;1. the version number is just sitting there in a json file.&lt;/strong&gt;&lt;br&gt;&#xA;This can be a pain, because what happens when you release a new version  ?well you either have to change this value or run some form of build script to change it for you. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;2. the JavaScript file never changes and might be cached between releases&lt;/strong&gt;&lt;br&gt;&#xA;Because the main JavaScript file is in this file it doesn&#x27;t change between releases, this means if someone updates your package to a new version this file URL is still the same and their browser might not fetch the newer version from the server.&lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  IPackageManaifestReader&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Another alternative is to use a &lt;code&gt;IPackageManifestReader&lt;/code&gt; (&lt;a href=&quot;https://dev.to/skttl/server-side-registering-of-package-manifest-in-umbraco-14-49go&quot;&gt;https://dev.to/skttl/server-side-registering-of-package-manifest-in-umbraco-14-49go&lt;/a&gt; by &lt;a class=&quot;mentioned-user&quot; href=&quot;https://dev.to/skttl&quot;&gt;@skttl&lt;/a&gt;) which allows you to register your package via your backend code (see &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/blob/v17/main/src/DoStuff.Client/Composers/DoStuffPackageManifestReader.cs&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffPackageManifestReader.cs&lt;/a&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PackageManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ReadPackageManifestsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;  &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Assembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAssembly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoStuffPackageManifestReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))?&lt;/span&gt;&#xA;   &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;   &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PackageManifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PackageManifest&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;      &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DoStuff.Client&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DoStuff with Umbraco client&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;n&quot;&gt;AllowTelemetry&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;n&quot;&gt;Version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;      &lt;span class=&quot;n&quot;&gt;Extensions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;          &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DoStuff Client Bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;          &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;DoStuff.Client.Bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;          &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;          &lt;span class=&quot;n&quot;&gt;js&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/App_Plugins/DoStuffClient/do-stuff-client.js?v=&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&#x2B;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;     &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You can see there is a little bit more code here but the basic information is the same as the &lt;code&gt;umbraco-package.json&lt;/code&gt; file. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;The key difference is we can fetch the version from the running assembly(dll).&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When you build your NuGet packages of your extension the version will be stamped on the dll&#x27;s in the package by NuGet&#x27;s &lt;code&gt;pack&lt;/code&gt; process. and once its there the code in your &lt;code&gt;IPackageManifestReader&lt;/code&gt; will use that version value to update your package version and add it to the end of the JavaScript file&#x27;s URL. meaning for each new version the browser will fetch the file again.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You need to remember to register your package manifest in a composer&lt;/p&gt;&#xA;&#xA;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&#xA;  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPackageManifestReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DoStuffPackageManifestReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Entry points Auth.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Another change that we will go into more detail in, when we talk about entry points and APIs is the code for setting up the authentication between sites. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;if you use the opinionated starter kit (or the umbraco-extension with the `-ex&#x27; flag) you will get the skeleton of the code for communicating with the server from your font end code. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;&#xA;_host.consumeContext(UMB_AUTH_CONTEXT, async (authContext) =&amp;gt; {&lt;br&gt;&#xA;  // Get the token info from Umbraco&lt;br&gt;&#xA;  const config = authContext?.getOpenApiConfiguration();&lt;/p&gt;&#xA;&#xA;&lt;p&gt;client.setConfig({&lt;br&gt;&#xA;    auth: config?.token ?? undefined,&lt;br&gt;&#xA;    baseUrl: config?.base ?? &quot;&quot;,&lt;br&gt;&#xA;    credentials: config?.credentials ?? &quot;same-origin&quot;,&lt;br&gt;&#xA;  });&lt;br&gt;&#xA;});&lt;br&gt;&#xA;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is fine and it works - but from a bit of experience there is another bit you can add, that helps to keep things fresh should you have the browser open all day&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ts&lt;br&gt;&#xA;// client interceptor will get the latest token for the auth&lt;br&gt;&#xA;// context for a request, so if the token has been refreshed&lt;br&gt;&#xA;// since we first got it, we&#x27;ll still have a valid token.&lt;br&gt;&#xA; client.interceptors.request.use(async (request, _options) =&amp;gt; {&lt;br&gt;&#xA;  const token = await authContext?.getLatestToken();&lt;br&gt;&#xA;  request.headers.set(&quot;Authorization&quot;,&lt;/code&gt;Bearer ${token}&lt;code&gt;);&lt;br&gt;&#xA;  return request;&lt;br&gt;&#xA;});&lt;br&gt;&#xA;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;&#xA;A client interceptor will run when the requests are fired and just make sure you have the latest token from the user send over as part of the request. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Again this isn&#x27;t something you have to do, everything works fine in the examples, this just smooths the odd edge I have seen during development.&lt;/p&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:20:43 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-getting-started-hge</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Getting Started</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-getting-started-hge</link>
                    <description><![CDATA[&lt;h2&gt;&#xA;  &#xA;  &#xA;  umbraco-extensions&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;If you are starting out on a new umbraco extension or package you will probably want to start with the umbraco-extension template that comes as part of the Umbraco templates. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;So if you haven&#x27;t already you will want the Umbraco templates&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet new install Umbraco.Templates@17.*&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;and once you have these templates installed you &#x27;could&#x27; just run the umbraco extension.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet new umbraco-extension -n MySuperNewUmbracoPackage&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;and this will crate you a new package to pay about with, but at this point you won&#x27;t have an Umbraco site just an extension, so you will probably want to think about that. &lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Opinionated starter kit template.&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;if you actually want to have a site and a bit more structure around your development then you might want to consider &lt;a class=&quot;mentioned-user&quot; href=&quot;https://dev.to/lottepitcher&quot;&gt;@lottepitcher&lt;/a&gt;&#x27;s &lt;a href=&quot;https://github.com/LottePitcher/opinionated-package-starter&quot; rel=&quot;noopener noreferrer&quot;&gt;Opinionated starter kit template&lt;/a&gt;, this adds stuff around the edges of the standard umbraco-extension, such as a test site, GitHub scripts and umbraco-marketplace files which will help when you come to release your super package. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;You can install the templates for the Opinonated starter kit like so:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet new install Umbraco.Community.Templates.PackageStarter&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;you can then start a new project&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet new umbracopackagestarter -n MySuperPackage&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;after this you get your extension and a test site, and scripts all nicely laid out for you. &lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Project Structure&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For the &lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main&quot; rel=&quot;noopener noreferrer&quot;&gt;DoStuffWithUmbraco repository&lt;/a&gt; that we are using as a reference for these blog posts you will notice there are a few slight differences with both the umbraco-extensions and the opinionated starter kit. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;These changes are not major, and for the most part you can ignore them, but for information we have laid the project out like so.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&#x2B;-- src&#xA;  &#x2B;-- DoStuff&#xA;  &#x2B;-- DoStuff.Client&#xA;  &#x2B;-- DoStuff.Core&#xA;&#x2B;-- demo&#xA;  &#x2B;-- DoStuff.Website&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  DoStuff.Client&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Client contains all the &quot;front end&quot; code and API controllers for the extension to render inside Umbraco. There is no real &#x27;business logic&#x27; inside this layer, it&#x27;s the UI and the c# code required to get data to the UI.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  DoStuff.Core&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The core project contains the backend logic for the application - this is where services, repositories, data layers etc all live. we sperate this out from the client, because then well it&#x27;s separate and we can in theory use the core logic of our code without the front end, or on a different front end should it ever change &#x1F627; &lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  DoStuff&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The parent to all of these projects is the &#x27;DoStuff&#x27; library - no really this has no code it in, but it would be the library that became the NuGet package that people would install (if we made the library a package). &lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &quot;DoStuff&quot; has dependencies on the .Client and .Core projects so when someone would install the &quot;DoStuff&quot; NuGet package that would also fetch the client and the core. &lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  DoStuff.Website&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Our website is off in a &#x27;demo&#x27; folder, just so we can have it separate from our project. If this wasn&#x27;t a reference library we would probably exclude the &#x27;demo&#x27; folder from the source repository, then the site is independent of our package&#x27;s code. &lt;/p&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:20:29 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-intro-39ke</guid>
                    <title>Battle scarred developer&#x27;s guide to Umbraco v17 - Intro</title>
                    <link>https://dev.to/kevinjump/battle-scarred-developers-guide-to-umbraco-v17-intro-39ke</link>
                    <description><![CDATA[&lt;h2&gt;&#xA;  &#xA;  &#xA;  Introduction&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;It&#x27;s almost two years now since I wrote a &lt;a href=&quot;https://dev.to/kevinjump/series/26221&quot;&gt;quite large series of blog posts about the then very new umbraco Backoffice&lt;/a&gt;. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;The early adopters&#x27; guide to the Umbraco Backoffice was really just a way for me to write down my thoughts and discoveries as I started the process of moving our packages to the brave new world.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As it turned out quite a few people found those posts useful, and they are still being referenced by people today (the &lt;a href=&quot;https://github.com/umbraco/Umbraco-CMS-Backoffice-Skills&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco.AI Skills&lt;/a&gt; even use them), but you can learn a lot in two years and Umbraco has gone from v14 to v17, and there has been loads of refinements and tweaks. as such the early adopters guide series are good, but they could be a lot better. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;So - now that we&#x27;ve come out of the other side of two years of upgrading and developing packages for the &quot;new&quot; Umbraco back office, I thought it was time to dust off the code and re-visit the guides. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;So today, we are starting the &quot;Battle scarred developer&#x27;s guide to Umbraco v17&quot; - this is a series of blog posts aimed at package and extension developers - giving them a leg up for a quick start, and tips and tricks on how to do things &#x27;right&#x27;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It&#x27;s also worth noting the Umbraco documentation has also improved greatly since we did the first round of posts - so you should check out &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/overview&quot; rel=&quot;noopener noreferrer&quot;&gt;https://docs.umbraco.com/umbraco-cms/customizing/overview&lt;/a&gt; it may already tell you what you need to know. &lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;All of the code referenced in these blogs is available via GitHub: &lt;br&gt;&#xA;&lt;a href=&quot;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/KevinJump/DoStuffWithUmbraco/tree/v17/main&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 25 Feb 2026 17:20:19 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=wX5uvDOfLUc</guid>
                    <title>umbraCoffee - Compose Yourself</title>
                    <link>https://www.youtube.com/watch?v=wX5uvDOfLUc</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;This time we&#x27;re joined by Umbraco CTO Filip Bech-Larsen, and Head of Orchestration Jordan McFarlane, to talk all things Umbraco Compose...&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;&#xA;// TODO]]></description>
                    <pubDate>Wed, 25 Feb 2026 13:10:06 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://dev.to/phil-whittaker/mcp-vs-agent-skills-why-theyre-different-not-competing-2bc1</guid>
                    <title>MCP vs Agent Skills: Why They&#x27;re Different, Not Competing</title>
                    <link>https://dev.to/phil-whittaker/mcp-vs-agent-skills-why-theyre-different-not-competing-2bc1</link>
                    <description><![CDATA[&lt;h2&gt;&#xA;  &#xA;  &#xA;  Introduction&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;When Agent Skills launched in October 2025, they sparked immediate debate in the AI tooling community. Some prominent voices, including developer Simon Willison, suggested Skills might be &quot;a bigger deal than MCP,&quot; noting his waning interest in MCPs due to their token consumption issues(&lt;a href=&quot;https://simonwillison.net/2025/Oct/16/claude-skills/&quot; rel=&quot;noopener noreferrer&quot;&gt;Claude Skills are awesome, maybe a bigger deal than MCP&lt;/a&gt;). The timing seemed damning for MCP&#x2014;barely a year after Anthropic introduced the Model Context Protocol in November 2024, a lighter-weight alternative emerged that appeared to solve MCP&#x27;s biggest problem: context window exhaustion.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I&#x27;ve heard plenty of people discount MCP now, thinking we should all just be using Skills instead. But here&#x27;s what we&#x27;ve learned after using both technologies side by side: Skills and MCPs aren&#x27;t competing solutions to the same problem. They&#x27;re fundamentally different architectures serving different purposes.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills excel at information delivery and adaptive context management, functioning as ephemeral clouds of knowledge that LLMs can pull from as needed. MCPs provide structured tool integration, giving LLMs deterministic ways to speak to the outside world through well-defined protocols. Rather than replacement, they serve complementary roles. And with MCP&#x27;s recent adoption of progressive discovery in January 2026, the original context efficiency advantage that Skills held has disappeared(&lt;a href=&quot;https://www.atcyrus.com/stories/mcp-tool-search-claude-code-context-pollution-guide&quot; rel=&quot;noopener noreferrer&quot;&gt;What is MCP Tool Search? The Claude Code Feature&lt;/a&gt;).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;What remains are two distinct approaches to two distinct challenges in LLM integration. Think of it like a painter&#x27;s easel&#x2014;different brushes for different strokes, different tools for different purposes. This article explains why Skills aren&#x27;t a replacement for MCP, and why both technologies matter for building the future of AI integration.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What is MCP and Why It Mattered&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;When Anthropic launched the Model Context Protocol in November 2024, it was genuinely revolutionary(&lt;a href=&quot;https://www.ajeetraina.com/one-year-of-model-context-protocol-from-experiment-to-industry-standard/&quot; rel=&quot;noopener noreferrer&quot;&gt;One Year of Model Context Protocol: From Experiment to Industry Standard&lt;/a&gt;). The protocol simplified the entire process of connecting LLMs to the outside world. Before MCP, tool calling was difficult, non-standardized, and inconsistent across platforms. MCP made it dramatically easier and enabled people to build integrations that actually worked.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The impact was immediate. Within months, thousands of MCPs were created and deployed&#x2014;tens of thousands, even hundreds of thousands. The protocol shipped with SDKs in Python, TypeScript, C#, and Java, making it accessible across the ecosystem. Major platforms adopted it quickly: ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot, and Visual Studio Code all added first-class MCP support(&lt;a href=&quot;https://www.ajeetraina.com/one-year-of-model-context-protocol-from-experiment-to-industry-standard/&quot; rel=&quot;noopener noreferrer&quot;&gt;One Year of Model Context Protocol: From Experiment to Industry Standard&lt;/a&gt;). By early 2026, MCP was seeing over 97 million monthly SDK downloads with more than 10,000 active servers deployed.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;MCP genuinely revolutionized how we connect LLMs to external systems. That&#x27;s what MCPs are&#x2014;and they&#x27;re brilliant at it.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Original Context Window Problem&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;But people quickly realized there were some serious downsides to MCP, especially as more developers got used to how LLMs actually work. The problem? Your context window&#x2014;that finite space where you can influence what the LLM will output&#x2014;is precious real estate. It&#x27;s critical that you manage it carefully and only put things in there that you need, because otherwise you&#x27;ll get hallucinations and unreliable outputs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When MCP first launched, it put everything into the context window upfront. Every tool&#x27;s name, description, output schema, input schema&#x2014;everything loaded immediately. That was extraordinarily wasteful. A single tool could consume 500 to 900 tokens before you&#x27;d even started any actual work.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With only a handful of tools, you could easily end up with no context available to do anything useful.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This became particularly painful for us at Umbraco when we looked at composing tools together to create magic&#x2014;MCPs where tools could combine to make impossible things happen. That led to a lot of tools. The Umbraco MCP currently has around 345 tools(&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/developer-mcp/available-tools&quot; rel=&quot;noopener noreferrer&quot;&gt;Available Tools - Umbraco Documentation&lt;/a&gt;). When you compose all those together, you end up consuming around 30,000 tokens just for tool definitions. That&#x27;s more than most entire context windows.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This was definitely seen as a critical design flaw that limited MCP usability and scalability.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Rise of Skills and Progressive Discovery&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Skills were introduced in October 2025, almost a year after MCP was created(&lt;a href=&quot;https://claude.com/blog/equipping-agents-for-the-real-world-with-agent-skills&quot; rel=&quot;noopener noreferrer&quot;&gt;Equipping agents for the real world with Agent Skills&lt;/a&gt;). They launched to considerable acclaim and gained traction incredibly quickly. And the reason Skills caught fire so fast? They had a clever trick up their sleeve.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Core Innovation: Progressive Discovery&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Skills introduced progressive discovery. The idea: start with the smallest amount of information possible and put only that into the context window. It&#x27;s remarkably context-efficient.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;At the start, the only information in context about a particular skill is the name and a very short description. That might take up between 20 and 50 tokens out of 200,000. Incredibly efficient. Then when the LLM decides it wants to use a particular skill, it pulls in the skill markdown file and the rest of the instructions linked to it. Brilliant!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In that skill markdown file, you can link to other files within the skill. The LLM only loads those if it decides it needs more information. There might be links in there, and it might decide to go out to the internet. This progressive discovery that Skills have as a key advantage made real waves.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills were released as an open standard by Anthropic on December 18, 2025, with enterprise partnerships including Canva, Notion, Figma, and Atlassian providing prebuilt skills(&lt;a href=&quot;https://claude.com/blog/equipping-agents-for-the-real-world-with-agent-skills&quot; rel=&quot;noopener noreferrer&quot;&gt;Equipping agents for the real world with Agent Skills&lt;/a&gt;). The approach quickly gained traction across agentic tools and platforms. Most agentic tools out there now support Skills.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When Skills first came out, Simon Willison said they could be a bigger deal than MCPs. He expected thousands upon thousands of Skills just like when MCP first launched, making a real impact on software engineering and development(&lt;a href=&quot;https://simonwillison.net/2025/Oct/16/claude-skills/&quot; rel=&quot;noopener noreferrer&quot;&gt;Claude Skills are awesome, maybe a bigger deal than MCP&lt;/a&gt;). I think that&#x27;s probably going to happen&#x2014;we&#x27;ve seen momentum building, and people are discovering how useful they can be.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But I very much disagree that they&#x27;re a replacement for MCP. I don&#x27;t think that&#x27;s the case at all.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  MCP&#x27;s Recent Catch-Up: Progressive Discovery Arrives&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In January 2026&#x2014;just last month&#x2014;Claude Code introduced something I&#x27;d been genuinely waiting for ever since Skills launched(&lt;a href=&quot;https://www.atcyrus.com/stories/mcp-tool-search-claude-code-context-pollution-guide&quot; rel=&quot;noopener noreferrer&quot;&gt;What is MCP Tool Search? The Claude Code Feature&lt;/a&gt;). Anthropic took the same progressive discovery trick that made Skills so context-efficient and applied it directly to MCP.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So when you load an MCP now, you get the name and description of each tool&#x2014;really small and compact, taking up around 20 to 50 tokens each. Exactly the same as Skills. Absolutely incredible.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Then if the LLM decides it wants to use a particular tool for the task at hand, it loads in the input schema, output schema, full description, and everything around it. That means you can host many more MCP tools, and it won&#x27;t decimate your context window. It makes your conversations far more efficient and practical.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The impact was immediate and measurable. Token overhead dropped by 85%&#x2014;from around 77,000 tokens to just 8,700 tokens for setups with 50&#x2B; tools(&lt;a href=&quot;https://www.atcyrus.com/stories/mcp-tool-search-claude-code-context-pollution-guide&quot; rel=&quot;noopener noreferrer&quot;&gt;What is MCP Tool Search? The Claude Code Feature&lt;/a&gt;). Tool calling accuracy improved significantly as well: Claude Opus 4 jumped from 49% to 74% accuracy, while Opus 4.5 went from 79.5% to 88.1%.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For me, this means that initial problem with MCP&#x2014;the concern that haunted it from launch&#x2014;has actually been solved. I think MCP has some catching up to do in terms of perception. But that context window problem? It&#x27;s gone.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Active Context Management: The Real Game-Changer&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This trick that Skills and now MCPs both use is something I call active context management. Last year, the phrase was &quot;context engineering&quot;&#x2014;ensuring your context window only includes what it needs for a given task. That&#x27;s genuinely hard work to make work well, and it&#x27;s time-consuming to curate exactly what you want into the context window to support the tasks you have.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Active context management is something different&#x2014;a step forward. This is what Skills and MCP tools now do: they allow you to put a small amount of information into context, and then if it needs to be used, the rest gets pulled in afterward.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The challenge is a bit of a chicken-and-egg situation. How much information do you put into the context window to make it obvious for the LLM when to trigger it and progressively load the rest? That&#x27;s really tricky to get right, and that&#x27;s the real benefit of active context management&#x2014;nailing that balance.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This active approach enables LLMs to work with dozens or hundreds of tools while minimizing context exhaustion. It&#x27;s the key innovation that makes modern LLM integration practical at scale.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  This Is Where They Diverge&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I firmly believe there&#x27;s a real divergence between Skills and MCPs. It&#x27;s important to recognize that there are things MCPs are better at doing than Skills, and vice versa. There are areas where you&#x27;d choose one over the other. This is where the boundary between determinism and non-determinism becomes the key differentiator.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Skills Are Ephemeral Information Clouds&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Skills, by their very nature, are extraordinarily easy to set up. It&#x27;s literally just a directory with a file in it&#x2014;that&#x27;s the minimum you need. They&#x27;re trivial to build and start using.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But this also means that Skills can be quite ephemeral. They have an element of non-determinism in them, which makes them absolutely amazing&#x2014;but at the same time, it makes them a little unstructured.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;What we&#x27;re finding is that Skills work best as information clouds. They&#x27;re things that exist to provide information. They&#x27;re really about automated context management&#x2014;bringing in the right context at the right time to improve the LLM&#x27;s ability to make good decisions across a whole range of tasks.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills support progressive discovery beautifully. You start with a single piece of information, and from there, it can link out to related context, code examples, and deeper guidance as needed. The LLM can reach into, pulling out as much or as little as it needs for the task at hand, following the most direct path to exactly the right context at the right time. It&#x27;s about information efficiency for the LLM, and it&#x27;s crucial to understand that&#x27;s one of the most important things about Skills.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Their advantage is their flexibility&#x2014;their ability to adapt and change, to be used as much or as little as needed. That&#x27;s really the essence of what Skills are and why they matter.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills excel at:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Supporting diverse, exploratory LLM tasks&lt;/li&gt;&#xA;&lt;li&gt;Providing knowledge libraries and best practices&lt;/li&gt;&#xA;&lt;li&gt;Delivering automated context management&lt;/li&gt;&#xA;&lt;li&gt;Offering information guidance that adapts to the conversation&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But they&#x27;re fundamentally different from what an MCP is.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  MCPs Are Defined, Structured Tools&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;MCP is really about tools. It&#x27;s about structure. It&#x27;s about defined, stable architecture. It&#x27;s about giving an LLM a deterministic way to connect to things.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With Skills, you have that boundary and ability to move between non-determinism and determinism. With MCP, you don&#x27;t really have that&#x2014;and that can actually be a really good thing. When you trigger a tool, you know you&#x27;re triggering it. It&#x27;s much easier to get tools that compose together to complete agentic flows. Whereas with Skills, that&#x27;s trickier and less predictable because triggering Skills can be somewhat uncertain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With MCP, because it&#x27;s a well-defined and structured architecture, you have SDKs for it. You can build proper structures with testing, helpers, core systems, shared code, and all the infrastructure that comes with mature software development. That&#x27;s something you really can&#x27;t do with Skills in the same way. They look similar, but they&#x27;re actually very different.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You can have MCPs that chain into other MCPs for hierarchical composition. You have MCPs that are hosted on the internet with persistent connections. You can do dynamic tool loading with infrastructure designed for it. You can&#x27;t do any of that with Skills.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So it may look like Skills are a basic thing&#x2014;and they&#x27;re not. Whereas MCP may be seen as enterprise or more grown-up&#x2014;and that&#x27;s not what it&#x27;s about at all. It&#x27;s about structure. MCPs are structured. They&#x27;re precise. They work in well-defined ways. They have hard edges. Whereas Skills don&#x27;t. Skills are there to provide information, and maybe provide a little bit of deterministic information as well.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;MCPs provide the technical capabilities that enable:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Enterprise integrations where reliability matters&lt;/li&gt;&#xA;&lt;li&gt;Complex multi-step processes with predictable workflows&lt;/li&gt;&#xA;&lt;li&gt;Agentic systems that compose tools together reliably&lt;/li&gt;&#xA;&lt;li&gt;Infrastructure with shared code, testing, and helpers&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The key strength of MCPs is structure and composability.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Where They Converge: Scripts and Code Execution&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;But there is a similarity&#x2014;something that confuses people about Skills and maybe led people to think Skills could replace MCPs. That&#x27;s the way you can put scripts into Skills and have script files run in the sandboxed environment. On first pass, it looks like these things could replace MCPs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But it&#x27;s not really the case.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Scripts in Skills are quite basic. I wouldn&#x27;t want to create full structures behind them that the skill can trigger, and it&#x27;s very difficult to share scripts between Skills or fit them into a stable architecture. Skills scripts are really there to allow a level of determinism&#x2014;to use them for things like reports.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Getting an LLM to create a report on something is difficult because it will give you a different answer every time. But when you need something deterministic, you can run a skill that will return the same data every time it makes that call, and then the LLM can use it with much less likelihood of hallucination.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Dynamic Code Execution: A False Equivalence&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With MCP, there&#x27;s this thing called code execution that emerged in November 2025(&lt;a href=&quot;https://www.anthropic.com/engineering/code-execution-with-mcp&quot; rel=&quot;noopener noreferrer&quot;&gt;Code execution with MCP: building more efficient AI agents&lt;/a&gt;). The idea is that the LLM generates code on the fly&#x2014;writing raw API calls, scripts, or queries at runtime rather than invoking pre-built tool endpoints. Instead of calling a well-defined MCP tool like &lt;code&gt;createContent(title, body)&lt;/code&gt;, the LLM dynamically writes a fetch call or script to hit an API endpoint directly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills are very good at that pattern too&#x2014;their sandboxed script environment can run LLM-generated code in a similar way. And the problem is, with code execution, you&#x27;re ignoring all the benefits of systemizing and the difference between an API endpoint and what the LLM is most efficient for.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You lose the optimization of well-defined tool schemas: parameter validation, consistent output formats, and composability between tools. You&#x27;re ignoring the work of manipulating and shaping API structures specifically to make them LLM-friendly. Raw API calls are often verbose, poorly documented for LLM consumption, and inconsistent.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Both Skills scripts and MCP code execution become less effective when used this way&#x2014;you trade reliability and composability for flexibility you rarely need.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is the problem. Skills are brilliant, but they&#x27;re brilliant at what they do. It&#x27;s the lack of structure that makes them unsuitable for providing tools that need to be well-defined and highly structured. That&#x27;s where the difference lies, because MCP is there to provide structure. It exists within a framework, within a collection of tools that are all similar, all there to support the same set of functionality. It&#x27;s also much easier to compose tools to bring the magic out.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The better approach: Use scripts for deterministic outputs where consistency matters, and use properly structured MCP tools for integration and action. Don&#x27;t treat dynamic code generation as a substitute for purpose-built tool architecture.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Where Umbraco Uses Skills&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&#x27;ve thought long and hard about this. Given what we&#x27;ve said here, we see Skills as ephemeral, like a cloud of information that the LLM can call on. It can pull as much or as little as it needs to extract the information.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We use Skills for information delivery&#x2014;functioning as libraries of knowledge and best practices. That information might include code examples. It might include scripts that make small calls somewhere. But it&#x27;s in no way structured in the way MCPs are.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Structured Skill Sets Aligned With Extension Points&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The Skills we have may be structured. There may be lots&#x2014;like the back office skills we&#x27;ve got. We have a structure of Skills defined to match the extension points in the back office. But there&#x27;s no shared code between them, and they&#x27;re really seen as a library of information.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We include real-world code examples in Skills&#x2014;runnable samples that developers and LLMs can use directly. These aren&#x27;t just snippets; they&#x27;re practical, executable examples of proper setup and implementation patterns. This grounds the LLM&#x27;s output in tested, working code rather than generated approximations, significantly reducing hallucination.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We also provide direct links to Umbraco source code and the UUI (Umbraco UI) component library as authoritative references. Skills point the LLM to actual source repositories and component libraries, grounding responses in the ultimate source of truth rather than potentially outdated or hallucinated documentation. This ensures best practices come directly from Umbraco&#x27;s own codebase and UI patterns, not from the LLM&#x27;s general training data.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That&#x27;s where I see Skills going&#x2014;there to provide information to the LLM and help manage the context it has given the task at hand.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Where Umbraco Uses MCPs&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I don&#x27;t think anything&#x27;s going to change in our strategy around MCP now that Skills are part of the agentic world. We&#x27;re going to continue using MCP to provide tools into Umbraco, to open up Umbraco so you can use an LLM to talk to it, manipulate it, compose it, and use it in amazing and interesting ways.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The Umbraco Developer MCP currently exposes over 330 tools spanning 36 endpoint groups, providing near-complete parity with the Umbraco Management API(&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/developer-mcp/available-tools&quot; rel=&quot;noopener noreferrer&quot;&gt;Available Tools - Umbraco Documentation&lt;/a&gt;). An LLM with access to this MCP can create document types, manage media, configure members, set up cultures, define data types&#x2014;essentially all the operations you&#x27;d normally perform through the back office interface or API calls.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re continuing to build MCPs based on our add-on products and to create MCPs around different use cases. I don&#x27;t see that changing at all because of Skills.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;From our perspective, I see them as quite different things with quite different use cases, and that&#x27;s definitely going to be apparent in how we use these two separate technologies at Umbraco with LLMs.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Can They Work Together?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I see MCP and Skills both being used at Umbraco and both being leveraged heavily to make using LLMs with Umbraco as easy and fulfilling as possible. I don&#x27;t see any problem with using them together either.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We have plans to create content modeling Agent Skills to help you develop your content structures and provide information, expertise, and best practices on how to set up and generate your own Umbraco sites.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Powerful Combination&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;What you&#x27;ll have is the skill providing all the knowledge, best practices, and information on how to do it right, and then the MCP actually doing the action. The skill is the brain&#x2014;it knows what to do, what to create, how to update things, and how to create good structures for content in Umbraco. The MCP is the muscle that actually implements it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That&#x27;s where I see things going&#x2014;an example of collaboration between the two. I see that as genuinely important.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Conclusion&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Skills and MCPs are very different things&#x2014;and both are critically important for the future of how LLMs integrate with and enhance Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Skills function as information clouds with flexible context management, excelling at knowledge delivery and adaptive context enrichment. MCPs provide structured tools with reliable composition and integration, excelling at deterministic workflows and system integration.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Claims that Skills replace MCPs misunderstand these architectural differences. Skills adoption doesn&#x27;t diminish MCP value&#x2014;they solve different problems. Now that both support progressive discovery, the context efficiency question is settled. What remains are the real differences: structure versus flexibility, determinism versus adaptation, tools versus knowledge.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The future belongs to thoughtful use of both. Neither technology is universally better. Architecture should drive the choice, not hype or convenience. When Skills and MCPs work together, each playing to its strengths, you get the most powerful LLM integration possible.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So when evaluating these technologies for your use case, don&#x27;t ask &quot;Which one is better?&quot; Ask &quot;Do I need information delivery or tool execution?&quot; The answer to that question will point you in the right direction.&lt;/p&gt;]]></description>
                    <pubDate>Tue, 24 Feb 2026 13:01:29 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/116125633721526211</guid>
                    <title>Get your Umbraco Spark ticket before midnight on 28th Feb</title>
                    <link>https://umbracocommunity.social/@umbracospark/116125633721526211</link>
                    <description><![CDATA[&#x23F3; 4 days to go! Get your Umbraco Spark ticket before midnight on 28th Feb to secure ALL perks and goodies.After midnight, perks won&#x2019;t be included &#x2014; don&#x2019;t miss out! &#x1F39F;&#xFE0F;https://www.tickettailor.com/events/umbukfdn/1896581#UmbracoSpark #Umbraco #Countdown #LastChance]]></description>
                    <pubDate>Tue, 24 Feb 2026 12:36:14 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.mediafinder</guid>
                    <title>DigitalWonderlab.MediaFinder</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.mediafinder</link>
                    <description><![CDATA[An Umbraco backoffice dashboard that lets you search for media items by name, size, or type.]]></description>
                    <pubDate>Mon, 23 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.backofficeannouncement</guid>
                    <title>DigitalWonderlab.BackofficeAnnouncement</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.backofficeannouncement</link>
                    <description><![CDATA[A backoffice announcement bar for Umbraco. Toggle on/off a simple text announcement visible to all backoffice users.]]></description>
                    <pubDate>Mon, 23 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.2</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.2</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.2</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Sun, 22 Feb 2026 18:39:58 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v16.0.3</guid>
                    <title>Skybrud.Umbraco.Redirects 16.0.3</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v16.0.3</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Sun, 22 Feb 2026 18:32:06 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v13.0.9</guid>
                    <title>Skybrud.Umbraco.Redirects 13.0.9</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v13.0.9</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Sun, 22 Feb 2026 18:21:20 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/usupport.usync</guid>
                    <title>uSupport.uSync</title>
                    <link>https://marketplace.umbraco.com/package/usupport.usync</link>
                    <description><![CDATA[uSupport is an feature rich ticketing system designed specifically for the Umbraco. It is a complete ticketing solution that enables users to create, manage, and view tickets and their associated comments. With uSupport, you can quickly and easily create and manage tickets. uSupport is highly customizable, allowing users to set up their own ticket types and define ticket statuses.]]></description>
                    <pubDate>Sun, 22 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/sbd.contentinsights</guid>
                    <title>SBD.ContentInsights</title>
                    <link>https://marketplace.umbraco.com/package/sbd.contentinsights</link>
                    <description><![CDATA[This package for Umbraco aims to provide insights into the content, specifically the created documents and document types, using Chart.js.]]></description>
                    <pubDate>Sun, 22 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.wearecogworks.com/innerworks/articles/umbraco-mvp-story</guid>
                    <title>What Being an Umbraco MVP Really Means</title>
                    <link>https://www.wearecogworks.com/innerworks/articles/umbraco-mvp-story</link>
                    <description><![CDATA[Journey to MVP | Ravi Motha | Innerworks]]></description>
                    <pubDate>Fri, 20 Feb 2026 11:38:05 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/phases.umbraco.neattip</guid>
                    <title>Phases.Umbraco.NeatTip</title>
                    <link>https://marketplace.umbraco.com/package/phases.umbraco.neattip</link>
                    <description><![CDATA[NeatTip is an Umbraco package that automatically converts long property descriptions into compact, interactive tooltips. Instead of cluttering your content editing interface with lengthy descriptions, NeatTip displays a small icon next to property labels. Click or hover the icon to view the full description in a styled tooltip. Reduces visual clutter, supports draggable tooltips, and only activates in content editing pages.]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/vettvangur.umbracoazurestorageprovider</guid>
                    <title>Vettvangur.UmbracoAzureStorageProvider</title>
                    <link>https://marketplace.umbraco.com/package/vettvangur.umbracoazurestorageprovider</link>
                    <description><![CDATA[Azure Blob Storage provider for Umbraco CMS.]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/vettvangur.umbracostorageproviders</guid>
                    <title>Vettvangur.UmbracoStorageProviders</title>
                    <link>https://marketplace.umbraco.com/package/vettvangur.umbracostorageproviders</link>
                    <description><![CDATA[Shared storage providers infrastructure for Umbraco CMS]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/vettvangur.umbracoazurestorageprovider.imagesharp</guid>
                    <title>Vettvangur.UmbracoAzureStorageProvider.ImageSharp</title>
                    <link>https://marketplace.umbraco.com/package/vettvangur.umbracoazurestorageprovider.imagesharp</link>
                    <description><![CDATA[Adds ImageSharp support using the Azure Blob Storage provider for Umbraco CMS.]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/tracking-bouldering-sessions-with-a-garmin-watch-app-and-umbraco-dashboard/</guid>
                    <title>Tracking Bouldering Sessions with a Garmin Watch App and Umbraco Dashboard</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/tracking-bouldering-sessions-with-a-garmin-watch-app-and-umbraco-dashboard/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/february/tracking-bouldering-sessions-with-a-garmin-watch-app-and-umbraco-dashboard/</guid>
                    <title>Tracking Bouldering Sessions with a Garmin Watch App and Umbraco Dashboard</title>
                    <link>https://owain.codes/blog/2026/february/tracking-bouldering-sessions-with-a-garmin-watch-app-and-umbraco-dashboard/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 20 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=VnxqTnMjSYE</guid>
                    <title>Umbraco Compose demo by CTO, Filip Bech-Larsen.</title>
                    <link>https://www.youtube.com/watch?v=VnxqTnMjSYE</link>
                    <description><![CDATA[In this demo, Umbraco CTO Filip Bech-Larsen showcases Umbraco Compose, a data orchestration platform built to power modern composable architecture.&#xA;&#xA;You&#x2019;ll see how Compose connects multiple data sources &#x2014; including Umbraco CMS, Google Sheets, Algolia search, and Cloudflare &#x2014; into a single GraphQL layer designed for scalability, performance, and maintainability. Instead of building custom integrations for every client, Compose centralizes and shapes data for delivery across websites, apps, dashboards, and other digital channels.&#xA;&#xA;Filip demonstrates how to ingest 10,000 products in seconds, achieve a 99 Lighthouse performance score, switch data sources without breaking the frontend, and unlock new search capabilities simply by updating a GraphQL query &#x2014; no backend rewrites required.&#xA;&#xA;If you&#x27;re building composable digital platforms and want fewer integrations, less maintenance, and faster iteration, this demo shows what composable done right looks like.&#xA;&#xA;Chapters:&#xA;00:00 The Problem: Rebuilding custom integrations for every customer&#xA;00:24 What Umbraco Compose Is: Data orchestration &#x2B; GraphQL at scale&#xA;02:21 Composable Architecture Demo: CMS, Google Sheets, Algolia, Cloudflare&#xA;04:54 High-Volume Ingestion &#x2B; Performance (10,000 products, Lighthouse 99)&#xA;06:29 Switching Data Sources &#x2B; Enhancing Search with GraphQL&#xA;&#xA;Discover more about Umbraco Compose: https://umbraco.com/products/umbraco-compose/]]></description>
                    <pubDate>Thu, 19 Feb 2026 07:12:54 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://medium.com/@procustomsoftware/why-choose-umbraco-development-services-in-2026-c0b262856be0?source=rss------umbraco-5</guid>
                    <title>Why Choose Umbraco Development Services in 2026</title>
                    <link>https://medium.com/@procustomsoftware/why-choose-umbraco-development-services-in-2026-c0b262856be0?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/why-choose-umbraco-development-services-in-2026-c0b262856be0?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*M59UFeIxPM31hYEAsgEBiQ.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;In 2026, digital performance defines business success. For UK B2B companies and growing enterprises, choosing the right CMS is critical&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/why-choose-umbraco-development-services-in-2026-c0b262856be0?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Wed, 18 Feb 2026 12:01:10 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://uskinned.net/blog/were-nominated-can-we-make-it-two-in-a-row/</guid>
                    <title>uSkinned Nominated for Best Website Builder | CMS Critic Awards</title>
                    <link>https://uskinned.net/blog/were-nominated-can-we-make-it-two-in-a-row/</link>
                    <description><![CDATA[Nominated for Best Website Builder | CMS Critic Awards&#xA;&#xA;We&#x2019;re pleased to share that uSkinned has been nominated once again for Best Website Builder in the CMS Critic Awards.&#xA;&#xA;Voting closes 27th February.]]></description>
                    <pubDate>Wed, 18 Feb 2026 11:25:15 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/116085859516756826</guid>
                    <title>Umbraco Spark Low Ticket Alert!</title>
                    <link>https://umbracocommunity.social/@umbracospark/116085859516756826</link>
                    <description><![CDATA[@umbfyi tip]]></description>
                    <pubDate>Tue, 17 Feb 2026 12:11:18 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/uprofile-february-2026-jenny-bradshaw/</guid>
                    <title>uProfile February 2026 - Jenny Bradshaw</title>
                    <link>https://umbraco.com/blog/uprofile-february-2026-jenny-bradshaw/</link>
                    <description><![CDATA[Meet Jenny Bradshaw - From &quot;Um-WHAT?&quot; to Umbraco MVP]]></description>
                    <pubDate>Tue, 17 Feb 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/blockfarmeditor.umbraco</guid>
                    <title>BlockFarmEditor.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/blockfarmeditor.umbraco</link>
                    <description><![CDATA[Block Farm Editor for Umbraco. Allows for easy on-page editing using built-in Umbraco data types and property editors.]]></description>
                    <pubDate>Tue, 17 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/truthwillout.umbraco.rtestylesmanager</guid>
                    <title>Truthwillout.Umbraco.RteStylesManager</title>
                    <link>https://marketplace.umbraco.com/package/truthwillout.umbraco.rtestylesmanager</link>
                    <description><![CDATA[A visual dashboard for managing Umbraco Rich Text Editor styles and colors. Easily add, edit, and organize text styles with color pickers, live previews, and automatic CSS generation.]]></description>
                    <pubDate>Tue, 17 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/contenttrace.umbraco</guid>
                    <title>ContentTrace.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/contenttrace.umbraco</link>
                    <description><![CDATA[Content activity tracking for Umbraco 17. Logs save and publish actions and displays them in a backoffice dashboard.]]></description>
                    <pubDate>Tue, 17 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/af.umbraco.azure.blob.media.storage</guid>
                    <title>AF.Umbraco.Azure.Blob.Media.Storage</title>
                    <link>https://marketplace.umbraco.com/package/af.umbraco.azure.blob.media.storage</link>
                    <description><![CDATA[Azure Blob media storage provider for Umbraco 15/16/17 (.NET 9/10). Built on Umbraco.StorageProviders.AzureBlob and Umbraco.StorageProviders.AzureBlob.ImageSharp, it configures Azure Blob media filesystem and ImageSharp cache with auto-composition (no Program.cs changes required), startup fail-fast checks (required settings, connectivity, container checks) and configurable ImageSharp cache-retention cleanup.]]></description>
                    <pubDate>Mon, 16 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.merchello</guid>
                    <title>Umbraco.Community.Merchello</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.merchello</link>
                    <description><![CDATA[Enterprise ecommerce for Umbraco - making enterprise ecommerce simple]]></description>
                    <pubDate>Mon, 16 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.multihotspot</guid>
                    <title>OC.MultiHotspot</title>
                    <link>https://marketplace.umbraco.com/package/oc.multihotspot</link>
                    <description><![CDATA[A property editor that allows a backoffice user to add multiple hotspots on an image along with descriptions. Render the image with hotspots on the frontend with Leaflet JS]]></description>
                    <pubDate>Mon, 16 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/millnut/umbraco_lts_upgrade_audit</guid>
                    <title>A CLI tool to audit Umbraco 13 LTS projects and estimate upgrade effort to Umbraco 17 LTS.</title>
                    <link>https://github.com/millnut/umbraco_lts_upgrade_audit</link>
                    <description><![CDATA[millnut/umbraco_lts_upgrade_audit: A CLI tool to audit Umbraco 13 LTS projects and estimate upgrade effort to Umbraco 17 LTS.]]></description>
                    <pubDate>Sat, 14 Feb 2026 11:04:22 Z</pubDate>
                        <category>social</category>
                        <category>gihthub</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uadvancedbutton</guid>
                    <title>uAdvancedButton</title>
                    <link>https://marketplace.umbraco.com/package/uadvancedbutton</link>
                    <description><![CDATA[Enterprise-level CTA system with advanced buttons, button groups, dropdown menus, analytics hooks, and full accessibility. Works in Block Grid, Block List, and as standalone ViewComponent.]]></description>
                    <pubDate>Sat, 14 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://jcdc.dev/blog/umbraco-17-package-updates</guid>
                    <title>Umbraco 17 Package Updates</title>
                    <link>https://jcdc.dev/blog/umbraco-17-package-updates</link>
                    <description><![CDATA[Explore my updated suite of packages for Umbraco 17 and enhance your next Umbraco project with tools for relations, custom trees, and content editor experience.]]></description>
                    <pubDate>Fri, 13 Feb 2026 20:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/abjerner/Limbo.Umbraco.BlockList/releases/tag/v13.0.3</guid>
                    <title>Limbo.Umbraco.BlockList 13.0.3</title>
                    <link>https://github.com/abjerner/Limbo.Umbraco.BlockList/releases/tag/v13.0.3</link>
                    <description><![CDATA[Umbraco package that extends the functionality of Umbraco&#x27;s build-in block list.]]></description>
                    <pubDate>Fri, 13 Feb 2026 10:20:28 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116057132584781368</guid>
                    <title>Umbraco Developer MCP 17.1.0 is here!</title>
                    <link>https://umbracocommunity.social/@umbraco/116057132584781368</link>
                    <description><![CDATA[Umbraco Developer MCP 17.1.0 is here! Enjoy precise content updates, fine-grained tool control, structured and validated tooling, improved testing, and more - laying a solid foundation for future AI-powered workflows with Umbraco. Release notes: https://github.com/umbraco/Umbraco-CMS-MCP-Dev/releases/tag/v17.1.0  Documentation: https://docs.umbraco.com/umbraco-cms/reference/developer-mcp #Umbraco]]></description>
                    <pubDate>Fri, 13 Feb 2026 09:03:27 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/announcing-umbraco-contributing-partners-2025/</guid>
                    <title>Announcing: Umbraco Contributing Partners 2025</title>
                    <link>https://umbraco.com/blog/announcing-umbraco-contributing-partners-2025/</link>
                    <description><![CDATA[64 Platinum &amp; Gold Partners have earned the badge of being this year&#x27;s Contributing Partners]]></description>
                    <pubDate>Fri, 13 Feb 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=PIryRhziY1o</guid>
                    <title>Merchello MVP Walkthrough For Umbraco v17&#x2B;</title>
                    <link>https://www.youtube.com/watch?v=PIryRhziY1o</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 12 Feb 2026 20:20:00 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.githubissues</guid>
                    <title>Our.Umbraco.GitHubIssues</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.githubissues</link>
                    <description><![CDATA[Our.Umbraco.GitHubIssues is a small Umbraco package that brings open GitHub issues from the Umbraco project right into your backoffice.]]></description>
                    <pubDate>Thu, 12 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/crumpled.robotstxt</guid>
                    <title>Crumpled.RobotsTxt</title>
                    <link>https://marketplace.umbraco.com/package/crumpled.robotstxt</link>
                    <description><![CDATA[A flexible, configuration-driven robots.txt solution for Umbraco v13, v14, v15, v16 and v17 that protects your non-production environments from search engine indexing by default. Supports multi-site configurations, environment-aware rules, and flexible user-agent patterns with zero code setup.]]></description>
                    <pubDate>Thu, 12 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=GR-NZra1528</guid>
                    <title>DevRel Deep Dive: Creating Backoffice Extensions with AI Skills</title>
                    <link>https://www.youtube.com/watch?v=GR-NZra1528</link>
                    <description><![CDATA[This week Sebastiaan and Lotte are joined by Phil Whittaker, AI Staff Engineer at Umbraco to talk about what AI &#x201C;skills&#x201D; are and how you can use them to extend the Umbraco Backoffice. Phil talks us through installing the Umbraco Backoffice Skills, and then Seb demos using them to create a GitHub User Picker property editor.&#xA;&#xA;&#x1F440; Umbraco Backoffice Skills on GitHub: https://github.com/umbraco/Umbraco-CMS-Backoffice-Skills]]></description>
                    <pubDate>Wed, 11 Feb 2026 15:11:36 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://forum.umbraco.com/t/released-my-github-action-for-umbraco-cloud-deployments/7445</guid>
                    <title>GitHub Action for Umbraco Cloud Deployments Released</title>
                    <link>https://forum.umbraco.com/t/released-my-github-action-for-umbraco-cloud-deployments/7445</link>
                    <description><![CDATA[Released my GitHub Action for Umbraco Cloud Deployments - Umbraco community forum]]></description>
                    <pubDate>Wed, 11 Feb 2026 14:57:41 Z</pubDate>
                        <category>community</category>
                        <category>forum</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.extensions.variables</guid>
                    <title>Our.Umbraco.Extensions.Variables</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.extensions.variables</link>
                    <description><![CDATA[Variables property editor for Umbraco]]></description>
                    <pubDate>Wed, 11 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.bettericons</guid>
                    <title>Umbraco.Community.BetterIcons</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.bettericons</link>
                    <description><![CDATA[BetterIcons is a modern, powerful property editor for Umbraco CMS that provides access to 200,000&#x2B; icons from popular icon libraries including Material Design Icons, Font Awesome, Bootstrap Icons, and 100&#x2B; more. Built with React for a smooth, responsive user experience with smart search, color customization, and zero configuration.]]></description>
                    <pubDate>Wed, 11 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.extensions.theming</guid>
                    <title>Our.Umbraco.Extensions.Theming</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.extensions.theming</link>
                    <description><![CDATA[Theme picker and view engine for Umbraco]]></description>
                    <pubDate>Wed, 11 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116045689979220965</guid>
                    <title>Submissions for the Umbraco Awards 2026 are now open!</title>
                    <link>https://umbracocommunity.social/@umbraco/116045689979220965</link>
                    <description><![CDATA[&#x1F3C6; Submissions for the #Umbraco Awards 2026 are now open! Show off your work, get recognized, and inspire others!&#x2705; Fast &amp; easy - no video required&#x1F449; Submit now: https://codegarden.umbraco.com/awards/umbraco-awards]]></description>
                    <pubDate>Tue, 10 Feb 2026 10:55:38 Z</pubDate>
                        <category>social</category>
                        <category>hq</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/umbraco-skills-ai-agents/</guid>
                    <title>Umbraco Skills for AI Agents - A Game Changer for Backoffice Development</title>
                    <link>https://skttl.dev/blog/umbraco-skills-ai-agents/</link>
                    <description><![CDATA[Umbraco Skills for AI Agents - A Game Changer for Backoffice Development | skttl.dev]]></description>
                    <pubDate>Tue, 10 Feb 2026 10:21:53 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/patrickdemooij9/SeoToolkit.Umbraco/releases/tag/6.1.0</guid>
                    <title>SeoToolkit 6.1.0 is now available for the latest Umbraco version</title>
                    <link>https://github.com/patrickdemooij9/SeoToolkit.Umbraco/releases/tag/6.1.0</link>
                    <description><![CDATA[uSync compatability, page title template, content checks and much more! SeoToolkit 6.1.0 is now available for the latest Umbraco version. Check it out if you are interested in easy to use SEO! https://github.com/patrickdemooij9/SeoToolkit.Umbraco/releases/tag/6.1.0]]></description>
                    <pubDate>Tue, 10 Feb 2026 08:34:13 Z</pubDate>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://www.phases.io/insights/ai-in-umbraco-in-2026-choices-governance-and-day-to-day-use/</guid>
                    <title>AI in Umbraco in 2026: choices, governance, and day-to-day use</title>
                    <link>https://www.phases.io/insights/ai-in-umbraco-in-2026-choices-governance-and-day-to-day-use/</link>
                    <description><![CDATA[https://www.linkedin.com/posts/phases_ai-in-umbraco-2026-governance-choice-and-activity-7426869425099792385-Jjq7?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw]]></description>
                    <pubDate>Tue, 10 Feb 2026 06:54:05 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/storage.healthchecks</guid>
                    <title>Storage.HealthChecks</title>
                    <link>https://marketplace.umbraco.com/package/storage.healthchecks</link>
                    <description><![CDATA[Health checks for monitoring and maintaining Umbraco media storage. Identifies duplicate files, large media, missing files, orphaned files and unused media items.]]></description>
                    <pubDate>Tue, 10 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbmetrics</guid>
                    <title>UmbMetrics</title>
                    <link>https://marketplace.umbraco.com/package/umbmetrics</link>
                    <description><![CDATA[Package Description]]></description>
                    <pubDate>Tue, 10 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/modulabry.umbraco.suite</guid>
                    <title>ModuLabry.Umbraco.Suite</title>
                    <link>https://marketplace.umbraco.com/package/modulabry.umbraco.suite</link>
                    <description><![CDATA[A streamlined property editor suite for Umbraco (verified stable for v14-v17). Includes Enum-driven and simple list components for Dropdowns, Checkboxes and RadioButtons with full localization support.]]></description>
                    <pubDate>Tue, 10 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbracoprism</guid>
                    <title>UmbracoPrism</title>
                    <link>https://marketplace.umbraco.com/package/umbracoprism</link>
                    <description><![CDATA[Multi-tenancy for Umbraco (v17&#x2B;) with dynamic branding and stateless identity. Prism resolves branding, identity, and content context at runtime based on the incoming domain.]]></description>
                    <pubDate>Sat, 07 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbracomediasnapshot</guid>
                    <title>UmbracoMediaSnapshot</title>
                    <link>https://marketplace.umbraco.com/package/umbracomediasnapshot</link>
                    <description><![CDATA[Umbraco Media Snapshot adds lightweight media version history to Umbraco by automatically adding a File Version History property (fileVersionHistory) to common media types and creating the required Media Snapshot data type during installation/upgrade.]]></description>
                    <pubDate>Sat, 07 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@aaronsadleruk/116014085027847668</guid>
                    <title>Umbraco SQL query to get a list of content using Macros in V13</title>
                    <link>https://umbracocommunity.social/@aaronsadleruk/116014085027847668</link>
                    <description><![CDATA[If anyone needs an #Umbraco SQL query to get a list of content using Macros in V13... I&#x27;ve dropped what I&#x27;ve used here:https://gist.github.com/AaronSadlerUK/f2bb83ccaf0b23840f6c06b45e536de1]]></description>
                    <pubDate>Thu, 05 Feb 2026 11:46:34 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/introducing-umbraco-ai</guid>
                    <title>Introducing Umbraco AI</title>
                    <link>https://mattbrailsford.dev/introducing-umbraco-ai</link>
                    <description><![CDATA[If you caught the Umbraco Winter keynote webinar, you&#x2019;ll have seen us announce something I&#x2019;ve been working on for a while now - Umbraco AI. I&#x2019;ve...]]></description>
                    <pubDate>Thu, 05 Feb 2026 11:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=OY5YtA3mP5U</guid>
                    <title>Umbraco Highlights 2025</title>
                    <link>https://www.youtube.com/watch?v=OY5YtA3mP5U</link>
                    <description><![CDATA[Creating and updating your website should be the least of your worries. With Umbraco, you get a content management system known and loved for its flexibility and great editing experience.&#xA;&#xA;Umbraco is easy to get started with install, set up, and host it yourself or let us take care of it all for you on Umbraco Cloud. https://try.umbraco.com&#xA;&#xA;Learn more about Umbraco on our website https://umbraco.com&#xA;Discover our Umbraco Community: https://community.umbraco.com/&#xA;Find the Umbraco source code on Github: https://github.com/umbraco/Umbraco-CMS&#xA;Visit the Umbraco Documentation: https://docs.umbraco.com]]></description>
                    <pubDate>Thu, 05 Feb 2026 10:47:52 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=JqLSJJwkj5o</guid>
                    <title>Case Study: Thames Water UK x Umbraco</title>
                    <link>https://www.youtube.com/watch?v=JqLSJJwkj5o</link>
                    <description><![CDATA[How Thames Water built a secure, scalable and high-performance digital platform with Umbraco.&#xA;&#xA;Thames Water, the largest water company in the UK, serves more than 16 million people and relies on a highly available digital platform to support customers during critical situations. In this case study, the team explains why they moved to Umbraco CMS and how the transition improved performance, stability, and cost efficiency.&#xA;&#xA;The video covers Thames Water&#x2019;s gradual migration approach, how risk was reduced during the transition, and how the intuitive editor experience enabled the content team to work efficiently with minimal training. The result is a modern, scalable solution that supports high traffic volumes, improves customer communication, and delivers long-term value through reduced infrastructure and licensing costs.]]></description>
                    <pubDate>Thu, 05 Feb 2026 10:32:55 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=GK-eGhzZYpw</guid>
                    <title>Umbraco Winter Keynote 2026</title>
                    <link>https://www.youtube.com/watch?v=GK-eGhzZYpw</link>
                    <description><![CDATA[At the Winter Keynote 2026, we look back on a strong year for Umbraco and share what&#x2019;s next for the platform, partners, and community.&#xA;&#xA;The keynote covers highlights from 2025, including company growth, product milestones, global expansion, and the continued strength of the Umbraco community. We introduce major announcements such as 24/5 support, a new partner portal, and ISO 27001 certification.&#xA;&#xA;A key focus is Umbraco&#x2019;s approach to AI - built for choice, flexibility, and real-world use - with live demos of new AI capabilities inside Umbraco. The keynote also features the launch of Umbraco Compose, a new data orchestration platform designed to simplify composable architectures at scale.&#xA;&#xA;Finally, we share what&#x2019;s coming next for the CMS, the ecosystem, and the community including events, roadmap highlights, and a look ahead to Codegarden 2026.]]></description>
                    <pubDate>Thu, 05 Feb 2026 08:13:37 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.debasish.tech/blogs/from-umbraco-13-to-17-my-upgrade-journey-and-lessons-learned/</guid>
                    <title>From Umbraco 13 to 17: My Upgrade Journey and Lessons Learned</title>
                    <link>https://www.debasish.tech/blogs/from-umbraco-13-to-17-my-upgrade-journey-and-lessons-learned/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 05 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/arroact.umbraco.graphql</guid>
                    <title>Arroact.Umbraco.GraphQL</title>
                    <link>https://marketplace.umbraco.com/package/arroact.umbraco.graphql</link>
                    <description><![CDATA[A powerful GraphQL API package for Umbraco with built-in authentication, interactive IDE dashboard, and comprehensive content/media querying capabilities. Features include lazy loading, BlockGrid support, MultiNodeTreePicker handling, and configurable API key authentication.]]></description>
                    <pubDate>Thu, 05 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/af.umbraco.s3.media.storage</guid>
                    <title>AF.Umbraco.S3.Media.Storage</title>
                    <link>https://marketplace.umbraco.com/package/af.umbraco.s3.media.storage</link>
                    <description><![CDATA[AWS S3 media storage provider for Umbraco 17 (.NET 10). Based on original Our.Umbraco.StorageProviders.AWSS3 project (not compatible with Umbraco 17), the new project is fully refactored, optimized and extended for .NET 10 and Umbraco 17. Includes S3-backed IFileSystem integration, media delivery middleware, ImageSharp provider/cache, localized upload validation and configurable cache-retention cleanup. For ease management, the cache folder replicates the media folder hierarchy, ensuring a one-to-one correspondence between each media folder and its cache folder.]]></description>
                    <pubDate>Thu, 05 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/ai-intentionally/</guid>
                    <title>AI, intentionally</title>
                    <link>https://umbraco.com/blog/ai-intentionally/</link>
                    <description><![CDATA[Discover how Umbraco is building AI with purpose, putting choice, governance, and long-term value at the centre of digital experiences.]]></description>
                    <pubDate>Thu, 05 Feb 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=tXdbHn14Gn8</guid>
                    <title>Umbraco Compose example project</title>
                    <link>https://www.youtube.com/watch?v=tXdbHn14Gn8</link>
                    <description><![CDATA[YouTube: Umbraco Compose example project: https://www.youtube.com/watch?v=tXdbHn14Gn8&#xA;]]></description>
                    <pubDate>Wed, 04 Feb 2026 09:22:23 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://dev.to/jasonelkin/mitigating-cve-2025-67288-in-umbraco-13-if-you-feel-you-need-to-bl3</guid>
                    <title>Mitigating CVE-2025-67288 in Umbraco 13 (if you feel you need to)</title>
                    <link>https://dev.to/jasonelkin/mitigating-cve-2025-67288-in-umbraco-13-if-you-feel-you-need-to-bl3</link>
                    <description><![CDATA[&lt;p&gt;Firstly, this is a really ill-informed CVE. If you&#x27;ve not read &lt;a href=&quot;https://forum.umbraco.com/t/cve-2025-67288-and-v13/7079/6?u=jasonelkin&quot; rel=&quot;noopener noreferrer&quot;&gt;my comment on the Umbraco Forum&lt;/a&gt;, I&#x27;ll repeat the key bits, otherwise feel free to scroll down to the good stuff.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A bit about CVE-2025-67288&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;If you want the details you can read the full CVE at &lt;a href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2025-67288&quot; rel=&quot;noopener noreferrer&quot;&gt;nvd.nist.gov&lt;/a&gt;. Umbraco is in the process of disputing it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Let me break down why I think it&#x27;s mostly nonsense...&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Remote Code Execution?&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;It&#x2019;s an attention grabbing PoC..&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Upload a crafted PDF file containing embedded JavaScript.&lt;/li&gt;&#xA;&lt;li&gt;Observe that JavaScript from the PDF executes in the browser.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But what does Umbraco have to do with that? You could upload the file anywhere - in fact, and click this link if you dare, &lt;a href=&quot;https://jasonelkin.github.io/docs/pdfs/success_xss.pdf&quot; rel=&quot;noopener noreferrer&quot;&gt;I&#x27;ve uploaded that file to a GitHub pages site&lt;/a&gt;.&lt;br&gt;&#xA;Does that mean GitHub is vulnerable to a CWE-434&#x2026;? No, and neither is Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is what a &lt;a href=&quot;https://cwe.mitre.org/data/definitions/434.html&quot; rel=&quot;noopener noreferrer&quot;&gt;CWE-434&lt;/a&gt; means...&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The product allows the upload or transfer of dangerous file types that are &lt;strong&gt;automatically processed within its environment&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Emphasis mine, but this &quot;CVE&quot; doesn&#x2019;t pass that test. Umbraco doesn&#x27;t process the PDF such that arbitrary code can be run on your Umbraco site.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, remote code execution worthy a CVSS score of 10.0? No.&lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Is is really XSS?&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Look more closely at the XSS (Cross Site Scripting) claim. Where does the JavaScript actually run? In the browser, sure, but it&#x2019;s not run in the context of the Umbraco site (or GitHub.io, if you were brave and clicked the link above). It&#x2019;s sandboxed by the browser and not treated like it&#x2019;s running on your domain at all.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is such a common misconception that Chromium has an FAQ titled &quot;Does executing JavaScript in a PDF file mean there&#x27;s an XSS vulnerability?&quot;. Spoiler alert, &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/&#x2B;/HEAD/docs/security/faq.md#Does-executing-JavaScript-in-a-PDF-file-mean-there_s-an-XSS-vulnerability&quot; rel=&quot;noopener noreferrer&quot;&gt;the answer is no&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  It&#x27;s JavaScript, but not as we know it&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;It doesn&#x2019;t have access to any important browser APIs - i.e. no fetch() or document.cookies etc. in fact if you look at the alert itself you&#x2019;ll see it&#x2019;s calling app.alert()&#x2026; that&#x2019;s not a web browser API but a PDF one :face_with_monocle:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you try and craft a more complex JavaScript payload you&#x2019;ll quickly find that the browser will block any APIs that could present a real risk to the user.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Even if you could craft malicious JavaScript that executes in a browser, that CVE would really be the browser&#x2019;s problem, not Umbraco&#x2019;s.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you&#x27;re feeling extra nerdy, you can &lt;a href=&quot;https://docs.google.com/document/d/1olIb-1IFVqP2fLTq0eAdW-aL-K2dDDZGDe5mZgHGfO8/edit?tab=t.0#heading=h.wwlum47z8pg1&quot; rel=&quot;noopener noreferrer&quot;&gt;have a read of the design doc for the PDF Viewer in Chromium&lt;/a&gt;. It talks about sandboxing, what kinds of JavaScript execution are allowed, and why.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Obviously there&#x2019;s still a potential social engineering/phishing angle with these crafted PDFs&#x2026; but that&#x2019;s a whole different issue.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But good news, you can still mitigate against it!&lt;/p&gt;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Enter the IFileStreamSecurityAnalyzer&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;It&#x27;s a simple interface you can implement that Umbraco will then run uploaded filestreams through so you can add any logic you want to check if the files are safe.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IFileStreamSecurityAnalyzer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// Indicates whether the analyzer should process the file&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// The implementation should be considerably faster than IsConsideredSafe&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;fileStream&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// Analyzes whether the file content is considered safe&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;fileStream&quot;&amp;gt;Needs to be a Read/Write seekable stream&amp;lt;/param&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;returns&amp;gt;Whether the file is considered safe&amp;lt;/returns&amp;gt;&lt;/span&gt;&#xA;    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsConsideredSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The docs &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/security/serverside-file-validation&quot; rel=&quot;noopener noreferrer&quot;&gt;have a nice example&lt;/a&gt; showing you how to protect against a potentially malicious SVG file.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In a similar fashion we can implement our own class that will read a PDF&amp;lt; look for any JavaScript, and return false when &lt;code&gt;IsConsideredSafe()&lt;/code&gt; is called.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Notice the &lt;code&gt;ShouldHandle()&lt;/code&gt; method only takes the stream. File extensions don&#x27;t necessarily match up with what&#x27;s actually in a file so we need to check the contents first to see if this is actually a PDF. A quick way of doing that is to parse just the file signature in the file header.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There&#x27;s a &lt;a href=&quot;https://github.com/neilharvey/FileSignatures/&quot; rel=&quot;noopener noreferrer&quot;&gt;package for that&lt;/a&gt;!&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet add package FileSignatures&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Then for the PDF reading, I&#x27;m using &lt;a href=&quot;https://uglytoad.github.io/PdfPig/&quot; rel=&quot;noopener noreferrer&quot;&gt;PdfPig&lt;/a&gt;:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;dotnet add package PdfPig&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Here I need to add a disclaimer. The following code is based on something I&#x27;m using in production but the PDF library I&#x27;m using has rather restrictive/expensive licensing and I wanted to give you a demo that &quot;Just Works&quot; with some friendly OSS libraries - so I asked AI to help me rewrite this for PdfPig.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PdfSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PdfSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;IFileFormatInspector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileFormatInspector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IFileStreamSecurityAnalyzer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_suspiciousKeys&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;AA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;// Additional Actions&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;OpenAction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// Actions triggered on document open&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;JS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;// JavaScript Stream&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;JavaScript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// Explicit JavaScript name&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;Launch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// Launch external applications&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;SubmitForm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// Form submission (potential data exfiltration)&lt;/span&gt;&#xA;        &lt;span class=&quot;s&quot;&gt;&quot;ImportData&quot;&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// Import external data&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsConsideredSafe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Position&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ParsingOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UseLenientParsing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;c1&quot;&gt;// Check the entire document structure starting from catalog&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Structure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Catalog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CatalogDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogWarning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Failed to parse PDF for security analysis&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;DictionaryToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;ArrayToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;IndirectReferenceToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckIndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;StreamToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StreamDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;ObjectToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckDictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DictionaryToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// Check for suspicious keys&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keyName&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_suspiciousKeys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;c1&quot;&gt;// Subtype Check: /S /JavaScript&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;S&quot;&lt;/span&gt; &#xA;                &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NameToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NameToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;JavaScript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;c1&quot;&gt;// Recurse into all dictionary values&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArrayToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CheckIndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReferenceToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndirectReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actualToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Structure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;refToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ContainsSuspiciousContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actualToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileFormatInspector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DetermineFileFormat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;And then to register it, use an IComposer:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegisterFileStreamSecurityAnalysers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IComposer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IUmbracoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recognised&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileFormatLocator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFormats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inspector&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FileFormatInspector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recognised&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFileFormatInspector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inspector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IFileStreamSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PdfSecurityAnalyzer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Notice I&#x27;m configuring the &lt;code&gt;FileFormatInspector&lt;/code&gt; to only look for PDFs, which is better for performance but you can add multiple file types or configure it to look for all types known by the library - have a look at &lt;a href=&quot;https://github.com/neilharvey/FileSignatures/&quot; rel=&quot;noopener noreferrer&quot;&gt;the GitHub Repo&lt;/a&gt; for more info.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With the above code implemented, try and upload a PDF containing JavaScript (like the perfectly benign one I linked to above), and you&#x27;ll get this lovely error message.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiyn5khiy0wekqkl8fx7b.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiyn5khiy0wekqkl8fx7b.png&quot; alt=&quot; &quot; width=&quot;800&quot; height=&quot;248&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You may get some false positives, but this was all about false positives really, wasn&#x27;t it...&lt;/p&gt;]]></description>
                    <pubDate>Wed, 04 Feb 2026 02:49:57 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.umbracoai</guid>
                    <title>Jumoo.TranslationManager.UmbracoAi</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.umbracoai</link>
                    <description><![CDATA[UmbracoAi Connector for Jumoo.TranslationManager]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.persistence</guid>
                    <title>Umbraco.AI.Persistence</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.persistence</link>
                    <description><![CDATA[EF Core persistence layer for Umbraco AI]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.core</guid>
                    <title>Umbraco.AI.Core</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.core</link>
                    <description><![CDATA[Contains core logic for Umbraco AI]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.startup</guid>
                    <title>Umbraco.AI.Agent.Startup</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.startup</link>
                    <description><![CDATA[Startup and DI registration for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.persistence.sqlserver</guid>
                    <title>Umbraco.AI.Persistence.SqlServer</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.persistence.sqlserver</link>
                    <description><![CDATA[SQL Server migrations for Umbraco AI persistence]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.core</guid>
                    <title>Umbraco.AI.Prompt.Core</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.core</link>
                    <description><![CDATA[Core domain models and services for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence.sqlite</guid>
                    <title>Umbraco.AI.Agent.Persistence.Sqlite</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence.sqlite</link>
                    <description><![CDATA[SQLite migrations for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.web</guid>
                    <title>Umbraco.AI.Agent.Web</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.web</link>
                    <description><![CDATA[Management API for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.startup</guid>
                    <title>Umbraco.AI.Prompt.Startup</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.startup</link>
                    <description><![CDATA[Startup and DI registration for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence.sqlserver</guid>
                    <title>Umbraco.AI.Prompt.Persistence.SqlServer</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence.sqlserver</link>
                    <description><![CDATA[SQL Server migrations for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.startup</guid>
                    <title>Umbraco.AI.Startup</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.startup</link>
                    <description><![CDATA[Contains startup logic for Umbraco AI]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence</guid>
                    <title>Umbraco.AI.Prompt.Persistence</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence</link>
                    <description><![CDATA[EF Core persistence layer for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence.sqlite</guid>
                    <title>Umbraco.AI.Prompt.Persistence.Sqlite</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.persistence.sqlite</link>
                    <description><![CDATA[SQLite migrations for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence</guid>
                    <title>Umbraco.AI.Agent.Persistence</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence</link>
                    <description><![CDATA[EF Core persistence layer for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/february/upgrading-umbraco-13-to-umbraco-17-issue/</guid>
                    <title>Upgrading Umbraco 13 to Umbraco 17 issue</title>
                    <link>https://owain.codes/blog/2026/february/upgrading-umbraco-13-to-umbraco-17-issue/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence.sqlserver</guid>
                    <title>Umbraco.AI.Agent.Persistence.SqlServer</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.persistence.sqlserver</link>
                    <description><![CDATA[SQL Server migrations for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt.web</guid>
                    <title>Umbraco.AI.Prompt.Web</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt.web</link>
                    <description><![CDATA[Management API for Umbraco AI Prompt]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/upgrading-umbraco-13-to-umbraco-17-issue/</guid>
                    <title>Upgrading Umbraco 13 to Umbraco 17 issue</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2026/february/upgrading-umbraco-13-to-umbraco-17-issue/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent.core</guid>
                    <title>Umbraco.AI.Agent.Core</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.core</link>
                    <description><![CDATA[Core domain models and services for Umbraco AI Agent]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.persistence.sqlite</guid>
                    <title>Umbraco.AI.Persistence.Sqlite</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.persistence.sqlite</link>
                    <description><![CDATA[SQLite migrations for Umbraco AI persistence]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.web</guid>
                    <title>Umbraco.AI.Web</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.web</link>
                    <description><![CDATA[Management API for Umbraco AI]]></description>
                    <pubDate>Wed, 04 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=KpwlMLOf14o</guid>
                    <title>Umbraco Compose Unboxing</title>
                    <link>https://www.youtube.com/watch?v=KpwlMLOf14o</link>
                    <description><![CDATA[Umbraco Compose eases the management of data flows in complex enterprise projects. &#xA;&#xA;It&#x2019;s the &#x201C;in between&#x201D; data orchestration layer between your tech stack and your frontend(s) that ensures clean and streamlined connections, freeing you from hours of setting up custom endpoints, ongoing maintenance, and time-consuming debugging.  &#xA;&#xA;In this unboxing video, Lasse and Allen provide you with a technical deep dive into how Umbraco Compose works.]]></description>
                    <pubDate>Tue, 03 Feb 2026 15:45:36 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=fgc8w85hbP0</guid>
                    <title>Umbraco Compose - From Complexity to Clarity</title>
                    <link>https://www.youtube.com/watch?v=fgc8w85hbP0</link>
                    <description><![CDATA[No more &quot;Spaghetti Knot&quot; Architecture. With Umbraco Compose, you can build Clean Composable Architecture. &#xA;&#xA;Some call it data orchestration. Others, a fully managed backend-for-frontend. We call it Umbraco Compose.&#xA;&#xA;Learn more about Umbraco Compose here: www.umbraco/compose.com]]></description>
                    <pubDate>Tue, 03 Feb 2026 15:45:22 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#124</guid>
                    <title>Skrift Issue #124</title>
                    <link>https://skrift.io/issues/#124</link>
                    <description><![CDATA[Featuring guest posts by Nathaniel Nunes on &quot;Using Serilog Enrichers with Umbraco&quot; and Georgina Bidder on &quot;Not Your Usual Compliance Monitoring Tool&quot;]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.commerce.paymentproviders.kustom</guid>
                    <title>Umbraco.Commerce.PaymentProviders.Kustom</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.commerce.paymentproviders.kustom</link>
                    <description><![CDATA[Kustom payment provider for Umbraco Commerce]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.prompt</guid>
                    <title>Umbraco.AI.Prompt</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.prompt</link>
                    <description><![CDATA[Prompt management plugin for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/introducing-umbraco-compose/</guid>
                    <title>Introducing Umbraco Compose</title>
                    <link>https://umbraco.com/blog/introducing-umbraco-compose/</link>
                    <description><![CDATA[We&#x2019;re proud to launch our new Data Orchestration Platform: Umbraco Compose. A platform that&#x2019;ll ease the management of data flows in complex enterprise projects. Discover how Compose can save you hundreds of development hours and struggles.]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.agent</guid>
                    <title>Umbraco.AI.Agent</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent</link>
                    <description><![CDATA[Agent management plugin for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.anthropic</guid>
                    <title>Umbraco.AI.Anthropic</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.anthropic</link>
                    <description><![CDATA[Anthropic provider for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.microsoftfoundry</guid>
                    <title>Umbraco.AI.MicrosoftFoundry</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.microsoftfoundry</link>
                    <description><![CDATA[Microsoft AI Foundry provider for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.amazon</guid>
                    <title>Umbraco.AI.Amazon</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.amazon</link>
                    <description><![CDATA[Amazon Bedrock provider for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uheroslider</guid>
                    <title>uHeroSlider</title>
                    <link>https://marketplace.umbraco.com/package/uheroslider</link>
                    <description><![CDATA[A commercial-grade, advanced hero slider solution for Umbraco 17&#x2B;. Features image/video support, custom animations per slide, scheduling with timezone support, festival scheduling, auto-slide, loop controls, and fallback images.]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.openai</guid>
                    <title>Umbraco.AI.OpenAI</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.openai</link>
                    <description><![CDATA[OpenAI provider for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.google</guid>
                    <title>Umbraco.AI.Google</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.google</link>
                    <description><![CDATA[Google provider for Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai</guid>
                    <title>Umbraco.AI</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai</link>
                    <description><![CDATA[Umbraco AI]]></description>
                    <pubDate>Tue, 03 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/http-over-named-pipes</guid>
                    <title>Solving Port Conflicts in AI-Assisted Development: HTTP over Named Pipes</title>
                    <link>https://mattbrailsford.dev/http-over-named-pipes</link>
                    <description><![CDATA[The way I work day to day has changed quite a lot over the last 6 months.&#xA;AI coding assistants like Claude Code aren&#x2019;t just speeding up individual...]]></description>
                    <pubDate>Mon, 02 Feb 2026 19:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/Umbraco.Community.UmbNav</guid>
                    <title>UmbNav 4.1.0 for Umbraco V17&#x2B;</title>
                    <link>https://www.nuget.org/packages/Umbraco.Community.UmbNav</link>
                    <description><![CDATA[UmbNav 4.1.0 for Umbraco V17&#x2B; is here!&#xA;&#xA;This update is a biggy:&#xA;&#x2705; Enhances drag and drop functionality.&#xA;&#x2705; Reworks the menu service.&#xA;&#x2705; Simplifies extension points.&#xA;&#x2705; Implements package migration for legacy data format.&#xA;&#x2705; Adds unit tests for increased code reliability.&#xA;&#x2705; Introduces code analysis and review workflows.&#xA;&#x2705; Bumps version number to 4.1.0.&#xA;&#x2705; IsActive extension method overhauled&#xA;&#xA;h5yr to hotchillicode  for the migration PR!&#xA;&#xA;Grab it here:&#xA;https://www.nuget.org/packages/Umbraco.Community.UmbNav]]></description>
                    <pubDate>Mon, 02 Feb 2026 13:10:16 Z</pubDate>
                        <category>community</category>
                        <category>nuget</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://dev.to/skttl/customizing-umbraco-modelsbuilder-output-1243</guid>
                    <title>Customizing Umbraco ModelsBuilder Output</title>
                    <link>https://dev.to/skttl/customizing-umbraco-modelsbuilder-output-1243</link>
                    <description><![CDATA[&lt;p&gt;Umbraco&#x27;s ModelsBuilder is a fantastic tool that generates strongly-typed models from your document types. But what if you want to customize the generated output? Maybe you want to add property alias constants or other custom code to your models.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this article, I&#x27;ll show you how to create a custom &lt;code&gt;ModelsGenerator&lt;/code&gt; that post-processes the generated files to add your own customizations.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Problem&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;When working with Umbraco, you often need to reference property aliases as strings - for example, when using &lt;code&gt;SetValue()&lt;/code&gt; while creating or editing content through the ContentService, or maybe you need the dynamic property access from &lt;code&gt;IPublishedContent&lt;/code&gt;. This means scattering magic strings throughout your codebase:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// setting a property value on IContent&lt;/span&gt;&#xA;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;pageTitle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;New Title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// checking for a property on IPublishedContent&lt;/span&gt;&#xA;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;featuredImage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;If a property alias changes, you&#x27;ll need to hunt down every occurrence. It would be much better to have strongly-typed constants for each property alias, so you get compile-time safety and IntelliSense support.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;ModelsBuilder does provide a way to get the property aliases from the document type, but it is rather verbose, and it requires a dependency on &lt;code&gt;IPublishedContentTypeCache&lt;/code&gt;:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;@using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Umbraco&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PublishedCache&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;@inject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPublishedContentTypeCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_contentTypeCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blocksPropertyAlias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ContentPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetModelPropertyType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_contentTypeCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Blocks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Solution&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We can create a custom &lt;code&gt;ModelsGenerator&lt;/code&gt; that runs after the standard generation and post-processes the output files. This approach:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Doesn&#x27;t require modifying Umbraco&#x27;s source code&lt;/li&gt;&#xA;&lt;li&gt;Works with the existing ModelsBuilder pipeline&lt;/li&gt;&#xA;&lt;li&gt;Allows for any kind of text manipulation on generated files&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s the full implementation:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.Extensions.Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.RegularExpressions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Core.Configuration.Models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Infrastructure.ModelsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Infrastructure.ModelsBuilder.Building&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyProject.ModelsGenerators&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyOwnModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;UmbracoServices&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;umbracoService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;IOptionsMonitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelsBuilderSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;OutOfDateModelsStatus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outOfDateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;IHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hostingEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;umbracoService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outOfDateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hostingEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IModelsGenerator&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modelsDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ModelsDirectoryAbsolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hostingEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modelsDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modelsDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*.generated.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFileContents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddPropertyAliasConstants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newFileContents&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAllText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newFileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;AddPropertyAliasConstants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;public new const string ModelTypeAlias =&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IndexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ordinal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;\[.*ImplementPropertyType\(&quot;&quot;([^&quot;&quot;]&#x2B;)&quot;&quot;\)\][\s\S]*?public\s&#x2B;(?:virtual\s&#x2B;)?[\w\.:&amp;lt;&amp;gt;]&#x2B;\s&#x2B;([A-Za-z0-9_]&#x2B;)\s*(?:{|=&amp;gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;public new static class ModelPropertyAliases\n\t\t{\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;propName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;\t\t\tpublic const string &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;propName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; = \&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\&quot;;\n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\t\t}\n\n\t\t&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  How It Works&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The custom generator does a few key things:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  1. Inheriting from ModelsGenerator&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;By inheriting from &lt;code&gt;ModelsGenerator&lt;/code&gt; and implementing &lt;code&gt;IModelsGenerator&lt;/code&gt;, we can override the generation behavior while still using all the built-in functionality.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyOwnModelsGenerator&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IModelsGenerator&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  2. Post-Processing Generated Files&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;GenerateModels()&lt;/code&gt; method first calls &lt;code&gt;base.GenerateModels()&lt;/code&gt; to run the standard generation, then iterates through all &lt;code&gt;*.generated.cs&lt;/code&gt; files to apply our customizations:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GenerateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GenerateModels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modelsDirectory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ModelsDirectoryAbsolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_hostingEnvironment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;modelsDirectory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*.generated.cs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// Post-process each file...&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  3. Adding Property Alias Constants&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;AddPropertyAliasConstants&lt;/code&gt; method uses regex to find all properties marked with &lt;code&gt;[ImplementPropertyType]&lt;/code&gt; and extracts both the alias and property name. It then generates a nested static class containing constants for each property:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ModelPropertyAliases&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PageTitle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pageTitle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FeaturedImage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;featuredImage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Blocks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;blocks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Registering the Custom Generator&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;To use your custom generator, you need to register it with Umbraco&#x27;s dependency injection. Add this to your startup configuration:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyOwnModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Or if you&#x27;re using a composer:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ModelsGeneratorComposer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IComposer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IUmbracoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyOwnModelsGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Using the Generated Constants&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Once the models are regenerated, you can use the constants throughout your code:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Before: magic strings&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;pageTitle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;New Title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// After: strongly-typed constants&lt;/span&gt;&#xA;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HomePage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelPropertyAliases&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PageTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;New Title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// Works great in LINQ queries too&lt;/span&gt;&#xA;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;featured&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Children&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ModelPropertyAliases&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FeaturedImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Now if a property alias changes, you&#x27;ll get a compile-time error instead of a runtime surprise.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Final Thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Customizing ModelsBuilder output gives you more control over your generated code without fighting against the built-in tooling. By post-processing the generated files, you can add compile-time safety features like property alias constants while keeping all the benefits of automatic model generation.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The approach shown here - inheriting from &lt;code&gt;ModelsGenerator&lt;/code&gt; and processing files after generation - is clean, maintainable, and survives Umbraco upgrades since it doesn&#x27;t modify any core code.&lt;/p&gt;]]></description>
                    <pubDate>Mon, 02 Feb 2026 11:33:05 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/umbraco-media-search-with-azure-ai-vision/</guid>
                    <title>Umbraco media search with Azure AI Vision</title>
                    <link>https://kjac.dev/posts/umbraco-media-search-with-azure-ai-vision/</link>
                    <description><![CDATA[Integrating Azure AI Vision and Umbraco Search to implement image content search powered by image recognition.]]></description>
                    <pubDate>Mon, 02 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.customvalidator</guid>
                    <title>Umbraco.Community.CustomValidator</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.customvalidator</link>
                    <description><![CDATA[A comprehensive document validation framework for Umbraco CMS that provides real-time content validation with a backoffice UI. Display validation results directly in the Umbraco backoffice with support for multi-culture content, severity levels (Error, Warning, Info), and automatic publish blocking when errors are present.]]></description>
                    <pubDate>Mon, 02 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/ai.diagnostics</guid>
                    <title>AI.Diagnostics</title>
                    <link>https://marketplace.umbraco.com/package/ai.diagnostics</link>
                    <description><![CDATA[AI Diagnostics analyzes application logs using AI to simplify complex issues by identifying possible root causes and providing actionable suggestions to fix them.]]></description>
                    <pubDate>Sun, 01 Feb 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/arrosimpleform</guid>
                    <title>ArroSimpleForm</title>
                    <link>https://marketplace.umbraco.com/package/arrosimpleform</link>
                    <description><![CDATA[Arro Simple Form is a tool that allows Umbraco users to create dynamic forms without writing code and integrate them into their Umbraco websites. It saves user-submitted data and can automatically send emails upon form submission.]]></description>
                    <pubDate>Sat, 31 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/djretailing.umbraco.suite</guid>
                    <title>DJRetailing.Umbraco.Suite</title>
                    <link>https://marketplace.umbraco.com/package/djretailing.umbraco.suite</link>
                    <description><![CDATA[A streamlined property editor suite for Umbraco (verified stable for v14-v17). Includes Enum-driven and simple list components for Dropdowns, Checkboxes and RadioButtons with full localization support.]]></description>
                    <pubDate>Sat, 31 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=Z8N-dXWdq04</guid>
                    <title>umbraCoffee - New Year, new U?!</title>
                    <link>https://www.youtube.com/watch?v=Z8N-dXWdq04</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;&#xA;// TODO]]></description>
                    <pubDate>Fri, 30 Jan 2026 11:00:08 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.debasish.tech/blogs/handling-editor-injected-javascript-in-umbraco-v17-with-umbraco-community-cspmanager/</guid>
                    <title>Handling editor-injected JavaScript in Umbraco v17 with Umbraco Community CSPManager</title>
                    <link>https://www.debasish.tech/blogs/handling-editor-injected-javascript-in-umbraco-v17-with-umbraco-community-cspmanager/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 30 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.debasish.tech/blogs/handling-editor-injected-javascript-in-umbraco-v17-with-umbracocommunitycspmanager/</guid>
                    <title>Handling editor-injected JavaScript in Umbraco v17 with Umbraco.Community.CSPManager</title>
                    <link>https://www.debasish.tech/blogs/handling-editor-injected-javascript-in-umbraco-v17-with-umbracocommunitycspmanager/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 30 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/@umbraco-cms/mcp-dev</guid>
                    <title>@umbraco-cms/mcp-dev</title>
                    <link>https://marketplace.umbraco.com/package/@umbraco-cms/mcp-dev</link>
                    <description><![CDATA[An MCP (Model Context Protocol) server for Umbraco CMS that unlocks AI-powered content management via the Umbraco Management API.]]></description>
                    <pubDate>Fri, 30 Jan 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/Our.Umbraco.TagHelpers/2.0.0</guid>
                    <title>TagHelpers v2.0.0 with Umbraco v16&#x2B; support</title>
                    <link>https://www.nuget.org/packages/Our.Umbraco.TagHelpers/2.0.0</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 29 Jan 2026 10:02:29 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>nuget</category>
                </item>
                <item>
                    <guid>https://www.jeroenbreuer.nl/blog/released-umbraco-compose-example-project</guid>
                    <title>Released: Umbraco Compose example project</title>
                    <link>https://www.jeroenbreuer.nl/blog/released-umbraco-compose-example-project</link>
                    <description><![CDATA[Released: Umbraco Compose example project - jeroenbreuer.nl]]></description>
                    <pubDate>Thu, 29 Jan 2026 08:03:27 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://discord.com/channels/869656431308189746/882981290662580264/1465425940669792502</guid>
                    <title>Find the right Umbraco icon with &quot;Umbraco Iconomancer&quot;</title>
                    <link>https://discord.com/channels/869656431308189746/882981290662580264/1465425940669792502</link>
                    <description><![CDATA[While fondly remembering going to the ucreate umbraco icons github page,&#xA;I did some digressing today trying to make the process of &quot;finding-the-right-icon&#x2122;&quot; even more enjoyable.&#xA;Meet the &quot;Umbraco Iconomancer&quot;, a Custom GPT with the sole purpose&#xA;of making your &quot;finding-the-right-icon&#x2122;&quot; experience umbazing. &#x1F601;&#x1F92D;&#xA;https://chatgpt.com/g/g-697788bd87888191aefe76e552c70820-umbraco-iconomancer]]></description>
                    <pubDate>Tue, 27 Jan 2026 17:24:19 Z</pubDate>
                        <category>community</category>
                        <category>discord</category>
                </item>
                <item>
                    <guid>https://dev.to/iovergaard/packagejson-as-single-source-of-truth-how-umbraco-auto-generates-typescript-paths-and-browser-101k</guid>
                    <title>Package.json as Single Source of Truth: How Umbraco Auto-Generates TypeScript Paths and Browser Import Maps</title>
                    <link>https://dev.to/iovergaard/packagejson-as-single-source-of-truth-how-umbraco-auto-generates-typescript-paths-and-browser-101k</link>
                    <description><![CDATA[&lt;h2&gt;&#xA;  &#xA;  &#xA;  Summary&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Building an extensible web application with 120&#x2B; shared packages creates a unique challenge: the same import must resolve to different locations during development (TypeScript source files), build time (external dependencies), and runtime (browser-served files). Traditional approaches require maintaining this mapping in three different config formats - TypeScript paths, test runner, and browser import maps - leading to inevitable configuration drift and an entire class of &quot;works in dev, breaks in production&quot; bugs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco CMS solves this through code generation: treating package.json exports as a single source of truth and automatically generating all configurations from it. A 200-line transformation script reads the exports field and produces TypeScript paths (with dist-cms &#x2192; src transformations), browser import maps (with dist-cms &#x2192; /umbraco/backoffice/ transformations), and test runner configs - all from one edit point. This makes configuration drift architecturally impossible: if TypeScript can find it, the browser can load it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The result is a pattern that scales to 120&#x2B; packages while maintaining correctness guarantees, enabling internal refactoring without breaking consumers, and providing the foundation for a professional, extensible package system built on web standards.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Problem with Raw File Paths&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;When building extensions for Umbraco&#x27;s backoffice, you&#x27;ve probably written code like this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UmbElementMixin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/backoffice/element-api&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UMB_NOTIFICATION_CONTEXT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/backoffice/notification&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;But have you ever wondered how this actually works?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You DO install the &lt;code&gt;@umbraco-cms/backoffice&lt;/code&gt; NPM package for types, yet the browser doesn&#x27;t load code from &lt;code&gt;node_modules&lt;/code&gt;. Instead, it loads from the Umbraco server. How does TypeScript know about the types during development, while the browser loads different files at runtime?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is the &lt;strong&gt;dual-resolution problem&lt;/strong&gt; - the same import must resolve to different locations at development time versus runtime. Umbraco solves it elegantly through a three-layer implementation combining TypeScript paths, Vite&#x27;s external configuration, and browser import maps.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Traditional Approach (And Why It&#x27;s Problematic)&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before Umbraco 14, you might have seen Umbraco extensions written with AngularJS having magical service injections based on strings like this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// &#x274C; Old approach: magic strings (entityService)&lt;/span&gt;&#xA;&lt;span class=&quot;nx&quot;&gt;angular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;umbraco&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;directive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;function &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;entityService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Do work with entityService&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You may even have imported modules of your own by virtue of Require.js or similar:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// &#x274C; Old approach: raw file paths&lt;/span&gt;&#xA;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/App_Plugins/MyPackage/utils.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This works, but it has several problems:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Brittle&lt;/strong&gt;: If you move files, all imports break&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Exposes Implementation&lt;/strong&gt;: Everyone sees your internal file structure&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Not Refactorable&lt;/strong&gt;: IDEs can&#x27;t help you rename or move things&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Unprofessional&lt;/strong&gt;: Looks like a hack rather than proper module architecture&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Hard to Type&lt;/strong&gt;: TypeScript struggles to provide IntelliSense for dynamic imports with string paths&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Umbraco Solution: Three Layers for Dual Resolution&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Umbraco CMS version 14 and beyond solves the dual-resolution problem (development vs runtime) through a three-layer implementation that makes imports work in different contexts:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Development (TypeScript)&lt;/strong&gt;: Resolves to source files for type-checking&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Build (Vite/Rollup)&lt;/strong&gt;: Marks as external, leaves imports as-is&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Runtime (Browser)&lt;/strong&gt;: Resolves to built files via importmap&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;Let&#x27;s see how each layer works.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Layer 1: TypeScript Resolution&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;TypeScript needs to know what &lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt; means for type-checking.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;For Umbraco Core Development&lt;/strong&gt; (working on the backoffice itself):&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/packages/core/notification/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This maps imports to source files for development on Umbraco itself.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note for Umbraco core developers&lt;/strong&gt;: This &lt;code&gt;tsconfig.json&lt;/code&gt; is auto-generated from &lt;code&gt;package.json&lt;/code&gt; exports by a build script. See the next section to understand how this works.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;For Extension Authors&lt;/strong&gt; (building backoffice extensions):&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Just install the NPM package:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt; @umbraco-cms/backoffice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;TypeScript automatically finds type definitions from &lt;code&gt;node_modules/@umbraco-cms/backoffice&lt;/code&gt; - no &lt;code&gt;paths&lt;/code&gt; config needed!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Full IntelliSense, type-checking, and refactoring support in your IDE.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt;: Because the package.json file contains an &lt;code&gt;&quot;exports&quot;&lt;/code&gt; field that defines the public API (more on that later).&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Layer 2: Build Resolution (Vite)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When building an extension, Vite needs to know not to bundle Umbraco&#x27;s code. &lt;strong&gt;This is the key step&lt;/strong&gt; - even though you installed the NPM package, you mark it as external:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// vite.config.ts&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;defineConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;rollupOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;                &lt;span class=&quot;sr&quot;&gt;/^@umbraco-cms/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Don&#x27;t bundle - even though it&#x27;s in node_modules!&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This tells Vite: &quot;When you encounter &lt;code&gt;@umbraco-cms/backoffice/*&lt;/code&gt;, &lt;strong&gt;don&#x27;t bundle it&lt;/strong&gt; from &lt;code&gt;node_modules&lt;/code&gt;. Leave the import statement as-is in the output.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Your built JavaScript contains &lt;code&gt;import(&#x27;@umbraco-cms/backoffice/notification&#x27;)&lt;/code&gt; exactly as written - &lt;strong&gt;no bundled code from the NPM package&lt;/strong&gt;. The NPM package was only used for TypeScript types during development.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is important for versioning. You may, in fact, multi-target your package regardless of the version of the installed NPM package, as it only provides types.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Layer 3: Runtime Resolution (Importmap)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When the browser loads your extension, it needs to know where to find the actual files:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;importmap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/umbraco/backoffice/packages/core/notification/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;This is defined in the backoffice&#x27;s package manifest and tells the browser: &quot;When you see an import for &lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt;, load this actual file.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The browser successfully loads the real JavaScript file at runtime.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  How It All Works Together&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Let&#x27;s follow an import through all three layers:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;You write:&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UMB_NOTIFICATION_CONTEXT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/backoffice/notification&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;During Development:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;TypeScript sees the import&lt;/li&gt;&#xA;&lt;li&gt;Finds types from the installed NPM package (&lt;code&gt;@umbraco-cms/backoffice&lt;/code&gt; in &lt;code&gt;node_modules&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Provides IntelliSense from the &lt;code&gt;.d.ts&lt;/code&gt; files&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Full type safety and autocomplete&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;During Build:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Vite encounters the import&lt;/li&gt;&#xA;&lt;li&gt;Checks &lt;code&gt;rollupOptions.external&lt;/code&gt; - matches &lt;code&gt;/^@umbraco-cms/&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Doesn&#x27;t bundle&lt;/strong&gt; the code from &lt;code&gt;node_modules&lt;/code&gt; - leaves import as-is&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Output contains: &lt;code&gt;import(&#x27;@umbraco-cms/backoffice/notification&#x27;)&lt;/code&gt; (no bundled code!)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;At Runtime:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Browser encounters the import&lt;/li&gt;&#xA;&lt;li&gt;Checks the importmap&lt;/li&gt;&#xA;&lt;li&gt;Resolves to &lt;code&gt;/umbraco/backoffice/packages/core/notification/index.js&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Loads the actual file&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Innovation: Code Generation from package.json&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Now here&#x27;s where Umbraco&#x27;s solution gets clever. With &lt;strong&gt;120&#x2B; packages&lt;/strong&gt; in the backoffice, manually maintaining TypeScript paths, Vite externals, and import maps would be a nightmare.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The Scale Problem&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Consider what&#x27;s required for each of those 120&#x2B; packages:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;TypeScript &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/strong&gt;: Map &lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt; to &lt;code&gt;./src/packages/core/notification/index.ts&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Vite config&lt;/strong&gt;: Mark as external (regex pattern handles this automatically)&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Runtime &lt;code&gt;umbraco-package.json&lt;/code&gt;&lt;/strong&gt;: Map &lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt; to &lt;code&gt;/umbraco/backoffice/packages/core/notification/index.js&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Test runner configs&lt;/strong&gt;: Various formats for Web Test Runner&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;That&#x27;s 120&#x2B; entries in &lt;code&gt;tsconfig.json&lt;/code&gt;, 120&#x2B; entries in the import map, and keeping them all in sync. A single typo breaks everything. Adding a new package requires updating multiple files in different formats.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;This is untenable.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Umbraco&#x27;s Solution: Single Source of Truth&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Umbraco solves this with code generation, treating &lt;code&gt;package.json&lt;/code&gt; as the &lt;strong&gt;golden source of truth&lt;/strong&gt;:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;package.json exports (source of truth)&#xA;    &#x2193;&#xA;devops/importmap/index.js (reads and transforms)&#xA;    &#x2193;&#xA;    &#x251C;&#x2192; devops/tsconfig/index.js &#x2192; tsconfig.json (120&#x2B; paths)&#xA;    &#x251C;&#x2192; devops/build/create-umbraco-package.js &#x2192; umbraco-package.json (import map)&#xA;    &#x2514;&#x2192; Test runner configuration&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpomeb8ku02tp6sh0cea.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpomeb8ku02tp6sh0cea.png&quot; alt=&quot;Diagram showing package.json exports at the top flowing through devops/importmap/index.js script in the middle, which outputs to three targets at the bottom: tsconfig.json for TypeScript paths, umbraco-package.json for browser import maps, and test runner configuration files. Arrows show the transformation from a single source to multiple config formats.&quot; width=&quot;800&quot; height=&quot;476&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;One edit point. Four outputs. Always in sync.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Why package.json as the Source?&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;package.json&lt;/code&gt; already defines the public API contract with its &lt;code&gt;exports&lt;/code&gt; field:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@umbraco-cms/backoffice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;./notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist-cms/packages/core/notification/index.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;./content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist-cms/packages/content/content/index.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;./modal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist-cms/packages/core/modal/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;117&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&#x2B;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;more&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why this is the perfect source:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Already required&lt;/strong&gt;: Published to npm, consumed by TypeScript naturally&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Defines the contract&lt;/strong&gt;: Lists every public import path&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Standard format&lt;/strong&gt;: Uses Node.js package exports (not a custom format)&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Single edit point&lt;/strong&gt;: Add/remove/rename a package in one place&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;&quot;.&quot;: null&lt;/code&gt; is intentional - it prevents importing the root package and forces consumers to use specific subpaths like &lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Implementation: The Transformation Pipeline&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s how Umbraco&#x27;s build scripts transform this single source into multiple formats.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Step 1: Create Import Map Structure&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;devops/importmap/index.js&lt;/code&gt; script reads &lt;code&gt;package.json&lt;/code&gt; exports and creates an intermediate import map:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createImportMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;imports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;additionalImports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Iterate over the exports in package.json&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;packageJsonExports&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// Skip null exports and non-JS files&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;moduleName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.\/&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;c1&quot;&gt;// Transform the path based on context&lt;/span&gt;&#xA;            &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rootDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.\/&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;dist-cms/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rootDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replaceModuleExtensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;importAlias&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;packageJsonName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;moduleName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;nx&quot;&gt;imports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;importAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;modulePath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;imports&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Step 2: Generate TypeScript Paths&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;devops/tsconfig/index.js&lt;/code&gt; script uses &lt;code&gt;createImportMap()&lt;/code&gt; with transformations for TypeScript:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;importmap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createImportMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;rootDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./src&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// dist-cms &#x2192; src&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;additionalImports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/internal/test-utils&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./utils/test-utils.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;replaceModuleExtensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// .js &#x2192; .ts&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// Convert to tsconfig.json format&lt;/span&gt;&#xA;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;importmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;imports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;nx&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// tsconfig expects arrays&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;nx&quot;&gt;tsConfigBase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;compilerOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;paths&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Generated output (&lt;code&gt;tsconfig.json&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/packages/core/notification/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/packages/content/content/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/modal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/packages/core/modal/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;117&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&#x2B;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;more&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Step 3: Generate Runtime Import Map&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;devops/build/create-umbraco-package.js&lt;/code&gt; script transforms for browsers:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;importmap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createImportMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;rootDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/umbraco/backoffice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// dist-cms &#x2192; server path&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;replaceModuleExtensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Keep .js for runtime&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// Write to umbraco-package.json&lt;/span&gt;&#xA;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/backoffice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;packageVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;importmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;imports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;importmap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;imports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Generated output (&lt;code&gt;umbraco-package.json&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@umbraco-cms/backoffice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;importmap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/umbraco/backoffice/packages/core/notification/index.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/umbraco/backoffice/packages/content/content/index.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@umbraco-cms/backoffice/modal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/umbraco/backoffice/packages/core/modal/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;117&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&#x2B;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;more&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The Key Transformations&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;From the same package.json export &lt;code&gt;&quot;./notification&quot;: &quot;./dist-cms/packages/core/notification/index.js&quot;&lt;/code&gt;, we generate:&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;Context&lt;/th&gt;&#xA;&lt;th&gt;Transformation&lt;/th&gt;&#xA;&lt;th&gt;Output&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/td&gt;&#xA;&lt;td&gt;&#xA;&lt;code&gt;dist-cms&lt;/code&gt; &#x2192; &lt;code&gt;src&lt;/code&gt;&lt;br&gt;&lt;code&gt;.js&lt;/code&gt; &#x2192; &lt;code&gt;.ts&lt;/code&gt;&#xA;&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;./src/packages/core/notification/index.ts&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;strong&gt;Runtime&lt;/strong&gt;&lt;/td&gt;&#xA;&lt;td&gt;&#xA;&lt;code&gt;dist-cms&lt;/code&gt; &#x2192; &lt;code&gt;/umbraco/backoffice/&lt;/code&gt;&lt;br&gt;Keep &lt;code&gt;.js&lt;/code&gt;&#xA;&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;/umbraco/backoffice/packages/core/notification/index.js&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;strong&gt;Package Name&lt;/strong&gt;&lt;/td&gt;&#xA;&lt;td&gt;Prepend package name and subpath&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;@umbraco-cms/backoffice/notification&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;Same logical structure. Different physical paths for different contexts.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Build Pipeline Integration&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;These scripts run at specific points in the development lifecycle:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;During development:&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm run generate:tsconfig  &lt;span class=&quot;c&quot;&gt;# Regenerates tsconfig.json from package.json&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Before build:&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm run generate:manifest  &lt;span class=&quot;c&quot;&gt;# Generates umbraco-package.json with import map&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;In CI/CD:&lt;/strong&gt;&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm run package:validate   &lt;span class=&quot;c&quot;&gt;# Validates that exports, paths, and imports are in sync&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Adding a new package:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Edit &lt;code&gt;package.json&lt;/code&gt;: Add one line to &lt;code&gt;exports&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Run &lt;code&gt;npm run generate:tsconfig&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Run &lt;code&gt;npm run generate:manifest&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Done &#x2705;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;No manual editing of configs. No risk of typos or drift.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The Result: Impossible to Have Config Drift&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Because everything is generated from the same source, it&#x27;s &lt;strong&gt;architecturally impossible&lt;/strong&gt; for configs to drift out of sync. If TypeScript can find it, the browser can load it. If the import map has it, TypeScript knows about it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This eliminates an entire class of bugs:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x274C; Import works in TypeScript but fails at runtime&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; Import works at runtime but TypeScript can&#x27;t find types&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; Test runner can&#x27;t resolve import that works everywhere else&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;With code generation, these bugs &lt;strong&gt;cannot exist&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why This Architecture Works at Scale&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The code generation approach provides significant advantages in production:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  1. &lt;strong&gt;Eliminates Config Drift&lt;/strong&gt;&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Manual maintenance inevitably leads to drift. A developer adds a TypeScript path but forgets the import map. Tests pass locally but production breaks.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With code generation, this is impossible. One source of truth means one failure mode: if &lt;code&gt;package.json&lt;/code&gt; is wrong, everything is wrong. Easy to catch, easy to fix.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  2. &lt;strong&gt;Maintainability&lt;/strong&gt;&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Manual approach:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;120 packages &#xD7; 3 configs = 360 entries to maintain&lt;/li&gt;&#xA;&lt;li&gt;Each in a different format&lt;/li&gt;&#xA;&lt;li&gt;Spread across 4&#x2B; files&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Generated approach:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;120 entries in &lt;code&gt;package.json&lt;/code&gt; (source of truth)&lt;/li&gt;&#xA;&lt;li&gt;1 script that generates everything else&lt;/li&gt;&#xA;&lt;li&gt;~200 lines of transformation logic (vs 360&#x2B; lines of config)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  3. &lt;strong&gt;Type Safety&lt;/strong&gt;&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;TypeScript provides full autocomplete and refactoring support. If you rename a package in &lt;code&gt;package.json&lt;/code&gt;, regenerate configs, and TypeScript immediately shows you every place that needs updating.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  4. &lt;strong&gt;Refactorability&lt;/strong&gt;&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Internal file structure can change without breaking consumers. As long as &lt;code&gt;package.json&lt;/code&gt; exports stay the same, consumers see a stable API. The generation scripts handle the internal path changes.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Example: Moving &lt;code&gt;notification&lt;/code&gt; from &lt;code&gt;packages/core/&lt;/code&gt; to &lt;code&gt;packages/alerts/&lt;/code&gt; requires:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Updating one path in &lt;code&gt;package.json&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Regenerating configs&lt;/li&gt;&#xA;&lt;li&gt;Zero changes for extension authors&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The NPM Package: Types and Code, But Not Bundled&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s where the pattern gets clever. Umbraco publishes an NPM package that contains &lt;strong&gt;both runtime JavaScript and TypeScript definitions&lt;/strong&gt;:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt; @umbraco-cms/backoffice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Extension authors install this package, but here&#x27;s the trick: they mark it as external in &lt;code&gt;vite.config.ts&lt;/code&gt; (as shown above), which &lt;strong&gt;prevents Vite from bundling the runtime code&lt;/strong&gt; even though it&#x27;s present in &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This means:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;TypeScript uses the package for types and IntelliSense during development&lt;/li&gt;&#xA;&lt;li&gt;Vite sees it&#x27;s external and doesn&#x27;t bundle it&lt;/li&gt;&#xA;&lt;li&gt;The browser loads the actual runtime code from the server at runtime&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;The complete picture:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Development&lt;/strong&gt;: TypeScript finds types from the installed NPM package&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Build&lt;/strong&gt;: Vite sees &lt;code&gt;external&lt;/code&gt; config and doesn&#x27;t bundle the code from &lt;code&gt;node_modules&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Runtime&lt;/strong&gt;: Browser uses importmap to load actual files from &lt;code&gt;/umbraco/backoffice/&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;The magic&lt;/strong&gt;: Same NPM package name (&lt;code&gt;@umbraco-cms/backoffice/*&lt;/code&gt;), but:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;In development &#x2192; resolved to types from &lt;code&gt;node_modules&lt;/code&gt; for IntelliSense&lt;/li&gt;&#xA;&lt;li&gt;At build &#x2192; marked external, not bundled&lt;/li&gt;&#xA;&lt;li&gt;At runtime &#x2192; resolved to server files via importmap&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why Alternatives Don&#x27;t Work&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;At this point, you might be wondering: &quot;If import maps create all this config duplication, why use them at all? Are there better alternatives?&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It&#x27;s a fair question. Let&#x27;s consider what else we could have done.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Alternative 1: Traditional NPM Package (Bundle Our Code)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;What if we published the runtime code to NPM and let extension authors bundle it?&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; @umbraco-cms/backoffice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Extension authors would import and &lt;strong&gt;bundle our code&lt;/strong&gt; into their extensions.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why this doesn&#x27;t work:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Code duplication&lt;/strong&gt;: Every extension bundles the same 22kb of Search code&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Version conflicts&lt;/strong&gt;: Extension A uses Search v1.0, Extension B uses v1.1 - both loaded&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;No shared state&lt;/strong&gt;: Global contexts can&#x27;t communicate across separately bundled instances&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Bundle bloat&lt;/strong&gt;: The backoffice loads the same code 5 times for 5 extensions&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Defeats code-splitting&lt;/strong&gt;: We carefully split global/core bundles - all wasted&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Verdict&lt;/strong&gt;: This defeats the entire architectural purpose. We&#x27;d be back to the jQuery plugin days where every extension bundles duplicate dependencies.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Alternative 2: Raw File Paths (Skip the Abstraction)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;What if we skipped logical imports and used raw paths?&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Extension authors would write:&lt;/span&gt;&#xA;&lt;span class=&quot;nx&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/umbraco/backoffice/packages/content/index.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbContentRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why this doesn&#x27;t work:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Brittle&lt;/strong&gt;: If we reorganize files, every extension breaks&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;No abstraction&lt;/strong&gt;: Exposes implementation details to consumers&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;No TypeScript support&lt;/strong&gt;: How do extension authors get types?&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Unprofessional&lt;/strong&gt;: Looks like a hack rather than proper package architecture&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Non-refactorable&lt;/strong&gt;: IDEs can&#x27;t help with renames or moves&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Alternative 3: Custom Vite Plugin&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;What if we built a Vite plugin that understands Umbraco packages?&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;umbracoPlugin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/vite-plugin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;defineConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;        &lt;span class=&quot;nf&quot;&gt;umbracoPlugin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;externalPackages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@umbraco-cms/backoffice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The plugin could auto-configure externals, generate TypeScript paths, and validate imports.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why this doesn&#x27;t solve it:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x26A0;&#xFE0F; &lt;strong&gt;Still need import maps&lt;/strong&gt;: Browsers still need runtime resolution somehow&lt;/li&gt;&#xA;&lt;li&gt;&#x26A0;&#xFE0F; &lt;strong&gt;Another tool to maintain&lt;/strong&gt;: We&#x27;d have to build, document, and support it&lt;/li&gt;&#xA;&lt;li&gt;&#x26A0;&#xFE0F; &lt;strong&gt;Adoption barrier&lt;/strong&gt;: Extension authors must learn our custom tooling&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Could reduce duplication&lt;/strong&gt;: Might generate configs from a single source&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Verdict&lt;/strong&gt;: This could be a nice developer experience improvement, but it doesn&#x27;t eliminate the fundamental need for import maps at runtime.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The Real Problem: Ecosystem Gap, Not the Pattern&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s the critical insight: &lt;strong&gt;The problem isn&#x27;t import maps - it&#x27;s that the tooling ecosystem hasn&#x27;t standardized on them yet.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Import maps are:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Browser-native&lt;/strong&gt;: &lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#import-maps&quot; rel=&quot;noopener noreferrer&quot;&gt;WHATWG standard&lt;/a&gt;, shipped in all modern browsers&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;Semantically correct&lt;/strong&gt;: Designed exactly for this use case&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; &lt;strong&gt;The future&lt;/strong&gt;: The direction web standards are moving&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But JavaScript tooling is:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Behind the curve&lt;/strong&gt;: TypeScript, Vite, NPM don&#x27;t understand import maps&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Fragmented&lt;/strong&gt;: Each tool has its own config format for the same concept&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; &lt;strong&gt;Slow to adapt&lt;/strong&gt;: Browser standards move faster than tooling&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re not working around a limitation of import maps - we&#x27;re working around &lt;strong&gt;tooling that hasn&#x27;t caught up to the browser standard.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  What Our Constraints Actually Are&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When building a package for third-party extension authors, we must support:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Type definitions at development time&lt;/strong&gt; - Extension authors need IntelliSense&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;No runtime bundling&lt;/strong&gt; - Can&#x27;t have code duplication and version conflicts&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Shared runtime state&lt;/strong&gt; - Global contexts must be singletons&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Standard tools&lt;/strong&gt; - Must work with TypeScript, Vite, standard browsers&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Clean API&lt;/strong&gt; - Professional package names, not raw file paths&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Import maps are the only solution that satisfies all five constraints.&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  What Would Actually Improve Things?&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco can control:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x2705; Build code generation scripts (we did this)&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Document the pattern for extension authors&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Provide templates and examples&lt;/li&gt;&#xA;&lt;li&gt;&#x26A0;&#xFE0F; Build Vite plugins to improve DX (nice-to-have, doesn&#x27;t eliminate import maps)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;The ecosystem needs:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x274C; Native import map support in TypeScript&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; Native import map support in Vite/Rollup&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; Standardized tooling that treats import maps as first-class&lt;/li&gt;&#xA;&lt;li&gt;&#x274C; Better coordination between browser standards and build tool vendors&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Until the tooling catches up, patterns like Umbraco&#x27;s code generation are the pragmatic solution.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Conclusion&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The import map pattern is more than just a clever trick - it&#x27;s a fundamental architectural pattern that solves the dual-resolution problem (development vs runtime) elegantly through a three-layer implementation. By using logical module identifiers with different resolution strategies for TypeScript, Vite, and browsers, Umbraco creates a professional, maintainable, and extensible package system.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The real innovation is treating &lt;code&gt;package.json&lt;/code&gt; as a single source of truth and generating all configs from it. This eliminates an entire class of bugs, improves developer velocity, and makes it architecturally impossible for configs to drift out of sync.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For 120&#x2B; packages, code generation isn&#x27;t just an optimization - it&#x27;s a requirement. Manual maintenance at this scale is untenable.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The three-layer system (TypeScript paths &#x2192; Vite externals &#x2192; import maps) might seem complex at first, but once you understand how each layer works, you&#x27;ll appreciate the elegance of the solution. It&#x27;s a pattern that makes modern web development with extensible packages possible.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Appendix A: Building Extensions with This Pattern&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The following sections provide guidance for developers who want to build Umbraco extensions using this pattern.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;You can use the same pattern for your own Umbraco packages. Here&#x27;s how you might implement it for a Search package:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  1. Choose Your Namespace&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Use &lt;code&gt;@organization/your-package&lt;/code&gt; to stay within a self-defined scope, if you eventually publish this to an NPM feed.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/search/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;core&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/search/g&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lobal&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  2. Configure TypeScript (&lt;code&gt;tsconfig.json&lt;/code&gt;)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Map logical imports to your source files:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;baseUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/global/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./src/core/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  3. Configure Vite (&lt;code&gt;vite.config.ts&lt;/code&gt;)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Mark your package as external so it&#x27;s not self-bundled:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;defineConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;search-global&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;src/global/search-global.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;search-core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;src/core/search-core.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;formats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;es&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;rollupOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;na&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^@umbraco-cms/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^@organization/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  4. Add Importmap (&lt;code&gt;umbraco-package.json&lt;/code&gt;)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Tell browsers where to find your built files:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Your.Package.Id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@organization/search&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;alias&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Your.Package.Bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/YourPackage/bundle.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;importmap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/YourPackage/search-global.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/YourPackage/search-core.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Note how we use the same namespace scope in the importmap as in TypeScript and package.json. This way everything aligns.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  5. Use Logical Imports in Manifests&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Now use logical imports instead of raw paths:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// &#x2705; New approach: logical imports&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manifests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UmbExtensionManifest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;My.Repository&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;na&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@organization/search/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MyRepository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})),&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// &#x2705; New approach: import values&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UMB_SEARCH_CONTEXT_TOKEN&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@organization/search/global&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UMB_SEARCH_CONTEXT_TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// logs: context token instance&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  6. Export Your Types (&lt;code&gt;package.json&lt;/code&gt;)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you want others to extend your package, publish TypeScript definitions:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@organization/search&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;./core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist/core/index.d.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist/core/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;./global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist/global/index.d.ts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./dist/global/index.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  When to Add Code Generation&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;For a small package with 2-3 subpaths, manual config maintenance is fine. But consider building a generation script (like Umbraco&#x27;s) when:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#x2705; You have 4&#x2B; subpaths&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; You&#x27;re frequently adding new exports&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; You&#x27;re building a package ecosystem&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; You want others to extend your work&lt;/li&gt;&#xA;&lt;li&gt;&#x2705; Config drift has caused bugs&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The script doesn&#x27;t need to be complex - just read &lt;code&gt;package.json&lt;/code&gt; exports and generate the various config formats. You can use Umbraco&#x27;s scripts as a reference: &lt;a href=&quot;https://github.com/umbraco/Umbraco-CMS/blob/main/src/Umbraco.Web.UI.Client/devops/importmap/index.js&quot; rel=&quot;noopener noreferrer&quot;&gt;devops/importmap/index.js&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Appendix B: Advanced Usage - Multiple Subpaths&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You can split your package into multiple logical subpaths:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GlobalContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@organization/search/global&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Repository&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@organization/search/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Helpers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@organization/search/utils&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Each subpath can have different loading characteristics:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;global&lt;/strong&gt;: Loaded upfront for event listeners&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;core&lt;/strong&gt;: Lazy-loaded on demand&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;utils&lt;/strong&gt;: Shared utilities&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Just add each to your importmap:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;importmap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/Search/global.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/Search/core.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/utils&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/Search/utils.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Benefits of Multiple Subpaths&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Code splitting&lt;/strong&gt;: Users only load what they use&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Lazy loading&lt;/strong&gt;: Core functionality loads on-demand&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Clear organization&lt;/strong&gt;: API surface is explicit&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Independent updates&lt;/strong&gt;: Update one subpath without affecting others&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Appendix C: Common Pitfalls&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  1. Forgetting to Mark as External&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you don&#x27;t mark your package as external in Vite config, it will bundle itself, breaking the lazy-load pattern:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// &#x274C; Wrong: Will self-bundle&lt;/span&gt;&#xA;&lt;span class=&quot;nx&quot;&gt;rollupOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;nl&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^@umbraco-cms/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Missing your own package!&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// &#x2705; Correct: Mark your own package as external&lt;/span&gt;&#xA;&lt;span class=&quot;nl&quot;&gt;rollupOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;na&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&#xA;        &lt;span class=&quot;sr&quot;&gt;/^@umbraco-cms/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;sr&quot;&gt;/^@organization/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Add this!&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  2. Mismatched Paths&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The tsconfig path must point to the &lt;strong&gt;exports file&lt;/strong&gt; (usually &lt;code&gt;index.ts&lt;/code&gt;), not the entry file:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight typescript&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// &#x274C; Wrong: Points to entry file&lt;/span&gt;&#xA;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@organization/search/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./src/core/search-core.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// &#x2705; Correct: Points to exports file&lt;/span&gt;&#xA;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@organization/search/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./src/core/index.ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  3. Missing Importmap Entry&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If you add a new subpath, don&#x27;t forget to add it to the importmap:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;importmap&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;imports&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/global&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/Search/global.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;@organization/search/core&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/App_Plugins/Search/core.js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;            &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Don&#x27;t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;forget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;subpaths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;here!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  4. Config Drift&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Manually maintaining multiple configs leads to drift. If you have more than a few subpaths, consider implementing code generation like Umbraco does.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#import-maps&quot; rel=&quot;noopener noreferrer&quot;&gt;Import Maps Specification (WHATWG HTML Standard)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/import-maps&quot; rel=&quot;noopener noreferrer&quot;&gt;Import Maps Explainer (WICG)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/overview&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco Backoffice Extension Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript Path Mapping&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://github.com/umbraco/Umbraco-CMS/tree/main/src/Umbraco.Web.UI.Client/devops&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco&#x27;s Code Generation Scripts&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;]]></description>
                    <pubDate>Tue, 27 Jan 2026 15:34:30 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115962199874893850</guid>
                    <title>Umbraco Spark Opportunity Programme applications close Wednesday 28 January</title>
                    <link>https://umbracocommunity.social/@umbracospark/115962199874893850</link>
                    <description><![CDATA[&#x23F0; Closes Wednesday 28 January 2026Our Opportunity Programme (with @MikeMasey at Huskey) offers free #Umbraco Spark tickets for people who might not otherwise attend tech conferences.&#x1F6A8; Only 2 tickets left&#x2714; No sponsored labels Apply or nominate:https://docs.google.com/forms/d/e/1FAIpQLSfeqxcsCukFFrW7KI5MZJROKB1KUNy1q5j_E1nPCmVG_x7Bxg/viewform]]></description>
                    <pubDate>Mon, 26 Jan 2026 15:55:44 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115961994895073918</guid>
                    <title>Grab your Umbraco Spark ticket before prices increase</title>
                    <link>https://umbracocommunity.social/@umbraco/115961994895073918</link>
                    <description><![CDATA[&#x23F3; 6 days left to grab Umbraco Spark tickets at &#xA3;125 &#x2B; VATA strong speaker line-up, practical sessions, and a brilliant Umbraco crowd.Prices increase at the end of January.&#x1F39F;&#xFE0F; https://www.tickettailor.com/events/umbukfdn/1896581&#x1F440; https://umbracospark.com/#what#UmbracoSpark #Umbraco #BristolTech #WebDev #DotNet]]></description>
                    <pubDate>Mon, 26 Jan 2026 15:08:15 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=HV5_7VGdLxg</guid>
                    <title>DevRel Deep Dive: Sitemap &amp; Robots.txt Packages from Blend Interactive</title>
                    <link>https://www.youtube.com/watch?v=HV5_7VGdLxg</link>
                    <description><![CDATA[This week Sebastiaan and Lotte look at two packages from Blend Interactive for adding and configuring your site&#x2019;s (or sites&#x2019;) sitemap.xml and robots.txt. During the demo Sebastiaan sets up Claude Code to use the Umbraco MCP Server, and uses that to save some time, or perhaps mouse clicks!&#xA;&#xA;&#x1F4E6; Blend &#x200B;Interactive &#x200B;Umbraco Sitemap on the Umbraco Marketplace: https://marketplace.umbraco.com/package/blendinteractive.umbraco.sitemap&#xA;&#x1F4E6; Blend &#x200B;Interactive &#x200B;Umbraco &#x200B;Robots&#x200B;Txt on the Umbraco Marketplace: https://marketplace.umbraco.com/package/blendinteractive.umbraco.robotstxt &#xA;&#xA;--------&#xA;&#x2753; Looking for help with your Umbraco projects? Visit the Forum&#xA; https://forum.umbraco.com&#xA; &#xA;&#x1F4AC; Want to chat with the friendly Umbraco Community? Join our Discord server&#xA; https://discord.umbraco.com&#xA; &#xA;&#x2709;&#xFE0F; Got a question (or topic suggestion) for the HQ Developer Relations team?&#xA; https://umbra.co/contact-devrel, or the &#x2018;contact-devrel&#x2019; channel on Discord&#xA; &#xA;#Umbraco]]></description>
                    <pubDate>Mon, 26 Jan 2026 13:49:49 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://dev.to/skttl/enhanced-event-logs-and-deployment-monitoring-for-kudu-in-umbraco-cloud-i66</guid>
                    <title>Enhanced Event Logs and Deployment Monitoring for Kudu in Umbraco Cloud</title>
                    <link>https://dev.to/skttl/enhanced-event-logs-and-deployment-monitoring-for-kudu-in-umbraco-cloud-i66</link>
                    <description><![CDATA[&lt;p&gt;When working with Umbraco Cloud, I occasionally need to access Kudu for debugging and deployment tasks. You can access Kudu by clicking the &quot;Kudu&quot; button in your site&#x27;s dashboard, or directly via the URL: &lt;code&gt;https://&amp;lt;site-name&amp;gt;.scm.&amp;lt;cloud-region&amp;gt;.umbraco.io&lt;/code&gt; (where the cloud region might be &lt;code&gt;euwest01&lt;/code&gt;, for example).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When a site fails to start or a deployment fails without clear errors, the event log in Kudu can provide crucial insights. However, the raw experience leaves much to be desired.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;To solve this problem, I created two userscripts that enhance the Kudu interface.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;What is a userscript?&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A userscript is a program, usually written in JavaScript, for modifying web pages to augment browsing. Uses include adding shortcut buttons and keyboard shortcuts, controlling playback speeds, adding features to sites, and enhancing the browsing history.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;To use a userscript, you need a userscript manager. There are several available, but the most popular is &lt;a href=&quot;https://www.tampermonkey.net/&quot; rel=&quot;noopener noreferrer&quot;&gt;Tampermonkey&lt;/a&gt;. Tampermonkey is available for Chrome, Firefox, and Edge.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Visualizing Event Logs&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzga0elryhsaqshmfcuul.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzga0elryhsaqshmfcuul.png&quot; alt=&quot;Screenshot of the Event Log Viewer provided by the Kudu Event Log Viewer userscript&quot; width=&quot;800&quot; height=&quot;599&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;First thing to look at is the event log of the site. The event log contains information from IIS, and tells you any problems when starting the site. It can also contain information about failed deployments, and other issues.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But, it&#x27;s not the most user-friendly experience. It&#x27;s a raw XML file, and you have to manually parse it.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The event log is located at &lt;code&gt;/LogFiles/eventlog.xml&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;It can be a massive XML file with thousands of entries&lt;/li&gt;&#xA;&lt;li&gt;The most recent (and relevant) information is at the bottom&lt;/li&gt;&#xA;&lt;li&gt;There&#x27;s no syntax highlighting or formatting&lt;/li&gt;&#xA;&lt;li&gt;You have to manually parse XML in your head&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;This userscript adds a new &quot;Event Log&quot; tab to the Kudu interface, providing a much better experience:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Parsed and formatted&lt;/strong&gt;: Events are displayed in a clean table format&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Most recent first&lt;/strong&gt;: The latest events appear at the top (no more scrolling)&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Color-coded&lt;/strong&gt;: Each event type has its own color for quick identification&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Structured columns&lt;/strong&gt;: Event time, type, message, and source are clearly separated&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The event log helps you diagnose issues like missing DLLs, IIS startup failures, configuration problems, and data-related errors.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  How to use it&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Install the Kudu Event Log Viewer userscript from my &lt;a href=&quot;https://github.com/skttl/umbraco-userscripts&quot; rel=&quot;noopener noreferrer&quot;&gt;umbraco-userscripts&lt;/a&gt; repository&lt;/li&gt;&#xA;&lt;li&gt;Navigate to your Umbraco Cloud Kudu interface (e.g., &lt;code&gt;*.scm.euwest01.umbraco.io&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Click the &quot;Event Log&quot; link in the navbar&lt;/li&gt;&#xA;&lt;li&gt;Click &quot;Load Event Log&quot; to fetch and display events&lt;/li&gt;&#xA;&lt;li&gt;Events are displayed in Bootstrap panels with full details&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Tracking Deployments&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsxb6l114rg30pcceh2q.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsxb6l114rg30pcceh2q.png&quot; alt=&quot;Screenshot of the Deployment Viewer provided by the Umbraco Cloud Deployment Viewer userscript&quot; width=&quot;800&quot; height=&quot;599&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When using CI/CD with Umbraco Cloud, deployment visibility is limited compared to traditional git push deployments. You lose the immediate feedback from git logs about build status and deployment progress.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Normally, when a deployment fails, you need to manually navigate through files in &lt;code&gt;/site/deployments&lt;/code&gt; to find:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Which deployment is currently active&lt;/li&gt;&#xA;&lt;li&gt;Individual deployment folders with logs and status files&lt;/li&gt;&#xA;&lt;li&gt;Scattered information across multiple locations&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The Deployment Viewer userscript consolidates all this information into a single dashboard:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Deployment status&lt;/strong&gt;: See the current state of all deployments&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Live logs&lt;/strong&gt;: View logs for each deployment&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Auto-refresh&lt;/strong&gt;: Enable live updates to monitor deployments in real-time&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Manual triggers&lt;/strong&gt;: Start a new deployment without making a git change&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  How to use it&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Install the Umbraco Cloud Deployment Viewer userscript from my &lt;a href=&quot;https://github.com/skttl/umbraco-userscripts&quot; rel=&quot;noopener noreferrer&quot;&gt;umbraco-userscripts&lt;/a&gt; repository&lt;/li&gt;&#xA;&lt;li&gt;Navigate to your Umbraco Cloud Kudu interface (e.g., &lt;code&gt;*.scm.euwest01.umbraco.io&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Click the &quot;Deployments&quot; link in the navbar&lt;/li&gt;&#xA;&lt;li&gt;View the latest deployment status and log&lt;/li&gt;&#xA;&lt;li&gt;Click &quot;Auto-refresh&quot; to enable live log and status updates&lt;/li&gt;&#xA;&lt;li&gt;Click &quot;Trigger New Deployment&quot; to start a new deployment&lt;/li&gt;&#xA;&lt;li&gt;Click on any deployment in the history table to view full details in a modal&lt;/li&gt;&#xA;&lt;li&gt;Click file counts to view the deployment manifest&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Final Thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;While Umbraco Cloud&#x27;s dashboard provides excellent visibility for most scenarios, these userscripts fill important gaps when you need to dig deeper. They transform Kudu from a basic file browser into a powerful debugging and deployment monitoring tool.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Both userscripts are available on &lt;a href=&quot;https://github.com/skttl/umbraco-userscripts&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;. Give them a try the next time you&#x27;re troubleshooting a deployment issue or investigating a site failure.&lt;/p&gt;]]></description>
                    <pubDate>Mon, 26 Jan 2026 09:11:18 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@lee/115950636394898597</guid>
                    <title>Umbraco Contentment 6.1.0 is out now</title>
                    <link>https://umbracocommunity.social/@lee/115950636394898597</link>
                    <description><![CDATA[#umbraco #Contentment 6.1.0https://github.com/leekelleher/umbraco-contentment/releases/tag/6.1.0TL;DR, bug fixes and new Liquid.js-powered &quot;Block Editor Custom View&quot; feature.]]></description>
                    <pubDate>Sat, 24 Jan 2026 15:03:32 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/unavbar</guid>
                    <title>uNavbar</title>
                    <link>https://marketplace.umbraco.com/package/unavbar</link>
                    <description><![CDATA[A commercial-grade, dynamic navigation solution for Umbraco 17&#x2B;. easily manage menus, branding, and responsive layouts from the backoffice.]]></description>
                    <pubDate>Sat, 24 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.johanreitsma.com/blogs/fix-draft-block-gridlist-items-after-upgrading-from-umbraco-13-to-17-vary-by-culture-element-types/</guid>
                    <title>Fix Draft Block Grid/List Items After Upgrading from Umbraco 13 to 17 (Vary by Culture Element Types)</title>
                    <link>https://www.johanreitsma.com/blogs/fix-draft-block-gridlist-items-after-upgrading-from-umbraco-13-to-17-vary-by-culture-element-types/</link>
                    <description><![CDATA[Upgrading from Umbraco 13 to 17 and suddenly seeing Draft blocks or empty content in other languages? This post shows the cause, the SQL fix to apply before upgrading, and how to validate everything after the migration....]]></description>
                    <pubDate>Fri, 23 Jan 2026 15:15:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115943593340257266</guid>
                    <title>Umbraco achieves ISO 27001 certification</title>
                    <link>https://umbracocommunity.social/@umbraco/115943593340257266</link>
                    <description><![CDATA[&#x1F389; Confetti alert!Even though we&#x2019;ve been playing with the big boys for years&#x2026; we&#x2019;ve now got the ISO 27001 certification to prove it! &#x2705;This means our product teams follow audited, structured, and continuously improved security practices, giving partners and customers even more reason to trust Umbraco in enterprise and regulated environments.A massive shout-out to the teams who made this happen &#x1F44F;Read more on https://umbra.co/4r3RDTT#Umbraco]]></description>
                    <pubDate>Fri, 23 Jan 2026 11:35:11 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.arroact.com/blogs/umbraco-security-vulnerabilities/</guid>
                    <title>Top 10 Umbraco Security Vulnerabilities that You Must know in 2026</title>
                    <link>https://www.arroact.com/blogs/umbraco-security-vulnerabilities/</link>
                    <description><![CDATA[Top 10 Umbraco Security Vulnerabilities that You Must know in 2026]]></description>
                    <pubDate>Fri, 23 Jan 2026 08:10:11 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/iso-27001/</guid>
                    <title>Umbraco&#x27;s product organisation achieves ISO 27001 certification</title>
                    <link>https://umbraco.com/blog/iso-27001/</link>
                    <description><![CDATA[The Umbraco product organisation achieves ISO 27001 certification to strengthen security and enterprise readiness.]]></description>
                    <pubDate>Fri, 23 Jan 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115938637999376821</guid>
                    <title>Rock Solid Knowledge continuing their support as a sponsor of UmbracoSpark 2026</title>
                    <link>https://umbracocommunity.social/@umbracospark/115938637999376821</link>
                    <description><![CDATA[Great to have Rock Solid Knowledge continuing their support as a sponsor of #UmbracoSpark 2026 &#x1F64C;It&#x27;s always a pleasure to work with teams that consistently invest in the Umbraco community.&#x1F440; We still have ONE sponsor spot available&#x1F39F;&#xFE0F; 2 free tickets &#x2B; extra tickets at &#xA3;75pp and more!More info:&#x1F517; https://umbracospark.com&#x1F517; https://www.rocksolidknowledge.com/#UmbracoSpark #Umbraco #BristolTech #TechEvents #WebDev #DotNet]]></description>
                    <pubDate>Thu, 22 Jan 2026 12:09:08 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.proworks.com/blog/archive/upgrading-from-umbraco-13-to-17-lts-lessons-from-the-trenches/</guid>
                    <title>Upgrading from Umbraco 13 to 17 LTS: Lessons from the Trenches</title>
                    <link>https://www.proworks.com/blog/archive/upgrading-from-umbraco-13-to-17-lts-lessons-from-the-trenches/</link>
                    <description><![CDATA[&lt;p&gt;Upgrades, upgrades, upgrades!&#xA0; Upgrades are a hot topic in the Umbraco community ever since version 17 was released last November. There are a lot of great reasons to upgrade to this major version besides simply moving to the latest Long-Term Supported (LTS) version. Our team here at ProWorks has a ton of Umbraco upgrade experience and are here to help you make your upgrade a success. This article will update as we learn about new challenges and find solutions. Ready? Let&#x27;s do this!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a type=&quot;external&quot; href=&quot;#final-thoughts&quot; title=&quot;See the changelog at the bottom if you&#x27;re coming back to this after a while&quot; data-anchor=&quot;#final-thoughts&quot;&gt;See the changelog at the bottom if you&#x27;re coming back to this after a while&lt;/a&gt; - &lt;em&gt;Updated: January 22, 2026&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Why Upgrade?&lt;/h2&gt;&#xA;&lt;p&gt;Let&#x27;s look at why you would upgrade to the latest v17 version:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Most recent LTS&#xA0;version&lt;/strong&gt;&lt;/em&gt; - stay up to date with security patches and easiest to keep up to date in the future. Umbraco 17 is supported through November 27, 2028.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Performance improvements&lt;/strong&gt;&lt;/em&gt; - The rebuilt HybridCache speeds up startups especially on large sites. The latest .NET version 10 framework also improves performance.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Modern back-office SPA&lt;/strong&gt;&lt;/em&gt; - Rebuilt back-office allows for standards-based extensions and future-proofed architecture that no longer relies on Angular or other external large frameworks.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Deep AI Chat Integrations with Umbraco MCP&lt;/strong&gt;&lt;/em&gt;&#xA0;- The official Umbraco MCP Server only works on version 17 and allows AI to &quot;talk&quot; to your specific Umbraco back-office to report on, add, or modify content.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Full Management API&lt;/strong&gt;&lt;/em&gt; - This allows for automation of everything that can be done by a content editor or developer in the back-office.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Load-balancing the Back-office&lt;/strong&gt;&lt;/em&gt; - This allows the back-office to scale out like the front-end and simplifies hosting load-balanced Umbraco websites.&lt;/li&gt;&#xA;&lt;li&gt;Read even more on the official v17 release article: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://umbraco.com/blog/umbraco-17-lts-release/&quot; target=&quot;_blank&quot; title=&quot;Umbraco 17 LTS: Final Release&quot;&gt;Umbraco 17 LTS: Final Release&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;There is really a lot of truly great features available in version 17 and many more on the horizon in 18 and 19. It&#x27;s a perfect time to get this upgrade done so those other upgrades are less painful later.&lt;/p&gt;&#xA;&lt;h2&gt;Prepare for Your Upgrade&lt;/h2&gt;&#xA;&lt;p&gt;A good result always starts with a good plan, right?&lt;/p&gt;&#xA;&lt;h4&gt;Current Umbraco Version?&lt;/h4&gt;&#xA;&lt;p&gt;First, you should assess where your site currently is. Are you on version 13? Or something older?&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 8-9:&lt;/strong&gt; You will need to upgrade to v10, then v13, then v17 - in fact, in general it is recommended to upgrade to each LTS version on your path to the latest. There may need to be custom migrations if the site uses Grid or other unsupported editors. Content migration may be preferable is some cases. v8 has API changes that could mean major code refactoring.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 10-12:&lt;/strong&gt; Upgrade to the latest v13, then to v17. Getting to v13 should be relatively straight-forward, but an assessment is always helpful for planning.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 13:&lt;/strong&gt; Upgrade to the latest version of v13, currently 13.13.0.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Learn more about each version here: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://releases.umbraco.com/all-releases&quot; target=&quot;_blank&quot; title=&quot;https://releases.umbraco.com/all-releases&quot;&gt;https://releases.umbraco.com/all-releases&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;(Please feel free to &lt;a type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;contact ProWorks&quot; data-anchor=&quot;#section-footer&quot;&gt;contact ProWorks&lt;/a&gt; and get our recommendations)&lt;/p&gt;&#xA;&lt;h4&gt;Using Outdated Property Editors?&lt;/h4&gt;&#xA;&lt;p&gt;If you are using any deprecated or legacy property editors, its best to update them to use the latest version in v13 before upgrading.&lt;/p&gt;&#xA;&lt;p&gt;Property editors to migrate to in v13:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;MediaPicker -&amp;gt; MediaPicker3&lt;/li&gt;&#xA;&lt;li&gt;Nested Content -&amp;gt; Block List&lt;/li&gt;&#xA;&lt;li&gt;Stacked Content -&amp;gt; Block List&lt;/li&gt;&#xA;&lt;li&gt;Grid -&amp;gt; Block Grid&lt;/li&gt;&#xA;&lt;li&gt;Macros -&amp;gt; Blocks (Or remove if unused)&lt;/li&gt;&#xA;&lt;li&gt;XPath in pickers -&amp;gt; Plan for a change to use the new dynamic root setup&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;By addressing these structural elements first, the content will migrate much easier to v17.&lt;/p&gt;&#xA;&lt;h4&gt;v17 Package Support?&lt;/h4&gt;&#xA;&lt;p&gt;Do you have Umbraco Packages installed (it&#x27;s likely)? Go to the Umbraco Marketplace (&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://marketplace.umbraco.com/&quot; target=&quot;_blank&quot; title=&quot;https://marketplace.umbraco.com/&quot;&gt;https://marketplace.umbraco.com/&lt;/a&gt;) or the package Repository and verify that each package supports v17. Many of the most used packages are ready for v17 now. Some are working on it and may have a beta version that likely works well. Some may not have any v17 support.&lt;/p&gt;&#xA;&lt;p&gt;If you&#x27;re unsure and its not clear from the repo, then asking on the Umbraco Forums (&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://forum.umbraco.com/&quot; target=&quot;_blank&quot; title=&quot;https://forum.umbraco.com/&quot;&gt;https://forum.umbraco.com/&lt;/a&gt;) would be a good way to see how others are dealing with this package. Is there a new version coming soon? Is there a new package that effectively replaces this one?&lt;/p&gt;&#xA;&lt;p&gt;As always,&#xA0;we can help give you a recommendation to move forward on. We have been doing this a long time and would love to help! &lt;a type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;Contact Us&quot; data-anchor=&quot;#section-footer&quot;&gt;Connect with us below&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;Custom/Bespoke Back-Office Extensions?&lt;/h4&gt;&#xA;&lt;p&gt;If your site has custom Angular.js extensions, then those will need to be re-written in the new Typescript/Lit/Web Components extension model. For the initial upgrade you can simply disable or remove them and address them after the main upgrade is complete.&lt;/p&gt;&#xA;&lt;h4&gt;Using Smidge?&lt;/h4&gt;&#xA;&lt;p&gt;Smidge was removed in v17 and you will need to reinstall it if used by any custom code or by the site in general. It is straight-forward to re-add to your site and you can learn more on the Github repo: &lt;a href=&quot;https://github.com/Shazwazza/Smidge&quot;&gt;https://github.com/Shazwazza/Smidge&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There is also an Umbraco-specific package that you can install here: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://marketplace.umbraco.com/package/umbraco.community.smidge&quot; target=&quot;_blank&quot; title=&quot;https://marketplace.umbraco.com/package/umbraco.community.smidge&quot;&gt;https://marketplace.umbraco.com/package/umbraco.community.smidge&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;TipTap or TinyMCE?&lt;/h4&gt;&#xA;&lt;p&gt;Umbraco has migrated to a new RTE component called TipTap. It will migrate all of your RTEs to this new editor by default unless you install a community package, &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://github.com/ProWorksCorporation/TinyMCE-Umbraco&quot; target=&quot;_blank&quot; title=&quot;TinyMCE.Umbraco Github Repository&quot;&gt;TinyMCE.Umbraco&lt;/a&gt;. This package will prevent the migration and keep the TinyMCE editor installed and used as in previous versions. &lt;a href=&quot;/blog/archive/tinymceumbraco-v17-released-and-ready-for-umbraco-17/&quot; title=&quot;TinyMCE.Umbraco v17 Released and Ready for Umbraco 17&quot;&gt;Read more about TinyMCE.Umbraco v17 in this post&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You will need to decide if you want to keep using TinyMCE or move to TipTap. If you have TinyMCE customizations or complex RTE setups, you may want to keep TinyMCE. The package also gives you the option of easily adding premium features like AI tools. TipTap is based on ProseMirror and is more lightweight so may be preferred in some use cases.&lt;/p&gt;&#xA;&lt;h4&gt;Use Umbraco Forms?&lt;/h4&gt;&#xA;&lt;p&gt;If you use Umbraco Forms, then you may need to migrate to the new licensing structure. It is now licensed annually instead of a one-time cost. Umbraco Forms is included with Umbraco Cloud so this is less of a concern if you are hosted on Umbraco Cloud. Your choice is to license Umbraco Forms going forward or remove it in favor of a different solution.&lt;/p&gt;&#xA;&lt;h4&gt;Review Official Documentation:&lt;/h4&gt;&#xA;&lt;p&gt;Take a look at the Version Specific Upgrades documentation guide in the Official Umbraco Documentation: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific&quot;&gt;Version Specific Upgrades&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;There may be other key breaking changes highlighted there that will be important for your site.&lt;/p&gt;&#xA;&lt;p&gt;Also, the Upgrade Details document has information on some edge cases and older versions as well: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/upgrade-details&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/upgrade-details&quot;&gt;Upgrade Details&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Pre-Upgrade Process&lt;/h2&gt;&#xA;&lt;p&gt;I&#x27;ll assume you have solutions for all the above issues and are ready to try out an upgrade! Great! Now, take a step to make sure you have all of this in place:&lt;/p&gt;&#xA;&lt;h4&gt;Backup Your Database / Media&lt;/h4&gt;&#xA;&lt;p&gt;Always backup your database before starting. You will miss something and may need to iterate a few times to get the migrations just right.&lt;/p&gt;&#xA;&lt;p&gt;Also, now is a good time to get a backup of the latest media and pull it locally.&lt;/p&gt;&#xA;&lt;h4&gt;Side-by-Side Setup&lt;/h4&gt;&#xA;&lt;p&gt;Ideally, you have a v13 site for reference running locally and your future v17 site ready to upgrade. These should be using separate databases and be able to run completely side-by-side. This lets you quickly iterate with v13 changes and re-run the v17 upgrade as needed. In addition, this allows you to quickly verify that the site looks and works as expected.&lt;/p&gt;&#xA;&lt;p&gt;If you&#x27;re using Git, make sure you have each version on separate feature branches.&lt;/p&gt;&#xA;&lt;h4&gt;Verify Dev Setup&lt;/h4&gt;&#xA;&lt;p&gt;Get your development setup ready for .NET 10 and Umbraco v17:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Install .NET 10 SDK&lt;/li&gt;&#xA;&lt;li&gt;Update Visual Studio (2022 v17.12&#x2B; or 2026)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4&gt;Verify Latest v13&lt;/h4&gt;&#xA;&lt;p&gt;One last check to make sure you&#x27;re on the latest Umbraco v13 release. If not, do this first.&lt;/p&gt;&#xA;&lt;h2&gt;Upgrade Time!&lt;/h2&gt;&#xA;&lt;p&gt;Let&#x27;s do this.&lt;/p&gt;&#xA;&lt;h4&gt;1. Update Project Target Frameworks&lt;/h4&gt;&#xA;&lt;p&gt;This can be done directly in the .csproj files through the TargetFramework element:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;    &amp;lt;!-- Change target framework --&amp;gt;&#xA;    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4&gt;2. Update Nuget Packages&lt;/h4&gt;&#xA;&lt;p&gt;Update all Umbraco packages to version 17.latest:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms.Web.Website&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If using Umbraco Cloud update these as well:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.StorageProviders.AzureBlob&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Deploy.Cloud&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Deploy.Contrib&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Remove these packages as they aren&#x27;t needed for v17:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Cms.PublicAccess&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Identity.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms.Web.BackOffice&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Update all other packages used including Umbraco Extensions like:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Community.Contentment&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Forms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Any other packages used that support v17&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4&gt;3. Delete Build Artifacts&lt;/h4&gt;&#xA;&lt;p&gt;Delete all of the&#xA0;&lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;obj&lt;/code&gt; directories in each of the projects of your solution. Umbraco has reported that Visual Studio&#x27;s &quot;Clean Solution&quot; option is sometimes not enough.&lt;/p&gt;&#xA;&lt;h4&gt;4. Code Changes&lt;/h4&gt;&#xA;&lt;p&gt;There are a few code changes needed to get the Solution compiling.&lt;/p&gt;&#xA;&lt;h5&gt;Program.cs:&lt;/h5&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Remove&#xA0;&lt;code&gt;UseInstallerEndpoints()&lt;/code&gt;&#xA0;as this is now automatically setup in v17&lt;/li&gt;&#xA;&lt;li&gt;Consider comparing a fresh installed Umbraco v17 &lt;strong&gt;Program.cs&lt;/strong&gt; with your current code and verify that you have the latest patterns in place. I have found other areas that were missing and easy to miss.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5&gt;_ViewImports.cshtml:&lt;/h5&gt;&#xA;&lt;div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Remove Smidge references:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/div&gt;&#xA;&lt;pre&gt;&lt;code&gt;@* REMOVE these lines: *@&#xA;@addTagHelper *, Smidge&#xA;@inject Smidge.SmidgeHelper SmidgeHelper&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h5&gt;Generated Models&lt;/h5&gt;&#xA;&lt;p&gt;If you used strongly typed models and have Umbraco generate them to code files in your project/solution, then the solution likely won&#x27;t compile.&lt;/p&gt;&#xA;&lt;p&gt;You have two options:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Delete all generated models from the directory in your project and set the &lt;code&gt;Umbraco.Cms.ModelsBuilder.ModelsMode&lt;/code&gt; to &quot;InMemoryAuto&quot; from &quot;SourceCodeAuto&quot; or &quot;SourceCodeManual&quot;. This will allow you to run the upgrade then switch it back and regenerate them after the v17 back-office is working. If you do this, you will need to install the &lt;code&gt;Umbraco.Cms.DevelopmentMode.Backoffice&lt;/code&gt; to allow the &quot;InMemoryAuto&quot; mode to work in v17.&lt;/li&gt;&#xA;&lt;li&gt;Tweak the v13 generated models so they compile and regenerate them after the v17 back-office is working. This involves:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Changing &lt;code&gt;IPublishedContent&lt;/code&gt; from:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;global::Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent Image { get; }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;To &lt;code&gt;MediaWithCrops&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;global::Umbraco.Cms.Core.Models.MediaWithCrops Image { get; }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Watch for &lt;code&gt;IEnumerable&lt;/code&gt; references as well as they need to be changed to use &lt;code&gt;MediaWithCrops&lt;/code&gt; as well. Then fix any other build errors that you see.&lt;/p&gt;&#xA;&lt;p&gt;Ultimately you want to run the back-office so you can regenerate them. So editing them manually is a temporary solution if you&#x27;re having trouble compiling the solution.&lt;/p&gt;&#xA;&lt;h4&gt;5. Configure Razor Files for .NET v10&lt;/h4&gt;&#xA;&lt;h5&gt;Verify Runtime Modes&lt;/h5&gt;&#xA;&lt;p&gt;.NET 10 deprecated runtime Razor compilation. Views are now compiled at build time by default.&lt;/p&gt;&#xA;&lt;p&gt;Verify your .csproj has the following:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;   &amp;lt;PropertyGroup&amp;gt;&#xA;      &amp;lt;CopyRazorGenerateFilesToPublishDirectory&amp;gt;true&amp;lt;/CopyRazorGenerateFilesToPublishDirectory&amp;gt;&#xA;   &amp;lt;/PropertyGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I recommend compiling your Razor files to find any issues at build time instead of at runtime. Remove these lines from your .csproj:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;      &amp;lt;RazorCompileOnBuild&amp;gt;false&amp;lt;/RazorCompileOnBuild&amp;gt;&#xA;      &amp;lt;RazorCompileOnPublish&amp;gt;false&amp;lt;/RazorCompileOnPublish&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;More on Runtime modes from the Umbraco Documentation: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you need to restore the runtime Razor view compilation or want to use &lt;code&gt;InMemoryAuto&lt;/code&gt; model building, then you will need to install this package:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;dotnet add package Umbraco.Cms.DevelopmentMode.Backoffice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Read more in the Version Specific Guide to determine if you need this package: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; data-anchor=&quot;#umbraco-17&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;6. Optional: Disable Custom Packages&lt;/h4&gt;&#xA;&lt;p&gt;If you are planning to migrate custom back-office packages after the initial upgrade is complete, then you could simply disable any custom code and exclude it from the project.&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Rename folders in &lt;code&gt;App_Plugins&lt;/code&gt; with custom extensions to have &quot;.disabled&quot; at the end of the folder name.&lt;/li&gt;&#xA;&lt;li&gt;Update the &lt;code&gt;App_Plugins&lt;/code&gt; content include entry in the .csproj to exclude any folders with &quot;.disabled&quot; at the end:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code&gt;      &amp;lt;Content Include=&quot;App_Plugins\**&quot; Exclude=&quot;App_Plugins\*.disabled\**&quot;&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If you have extensions added via Razor Class Library, you may need to remove the reference or delete the .dll from the &lt;code&gt;bin&lt;/code&gt; folder. &#xA0;&lt;/p&gt;&#xA;&lt;h4&gt;7. Optional: Explicitly Set LocalTimeZone&lt;/h4&gt;&#xA;&lt;p&gt;The change to use UTC by default for System Dates may have implications for your site. Read more in the Version Specific Guide on whether you want to explicitly set a server time zone in appsettings.json.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; data-anchor=&quot;#umbraco-17&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&lt;/a&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;{&#xA;   &quot;Umbraco&quot;: {&#xA;      &quot;CMS&quot;: {&#xA;         &quot;SystemDateMigration&quot;: {&#xA;            &quot;LocalServerTimeZone&quot;: &quot;Eastern Standard Time&quot;&#xA;         }&#xA;      }&#xA;   }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4&gt;8. Licensing Changes&lt;/h4&gt;&#xA;&lt;p&gt;If you are using Umbraco Deploy or Umbraco Forms, then you will need to migrate the licensing from the file-based .lic to the configuration settings in appsettings.json&lt;/p&gt;&#xA;&lt;h5&gt;Remove .lic Files&lt;/h5&gt;&#xA;&lt;p&gt;Remove the &lt;code&gt;.lic&lt;/code&gt; files inside of the &lt;code&gt;umbraco/Licenses&lt;/code&gt; folder for Umbraco Deploy and Umbraco Forms.&lt;/p&gt;&#xA;&lt;h5&gt;Add Licensing Configuration&lt;/h5&gt;&#xA;&lt;p&gt;For Umbraco Cloud sites, you can add these settings to your appsettings.json file. If you have a different hosting setup, you will need to replace &quot;UMBRACO-CLOUD&quot; with your license key.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;{&#xA;&#xA0; &quot;Umbraco&quot;: {&#xA;&#xA0; &#xA0; &quot;Licenses&quot;: {&#xA;&#xA0; &#xA0; &#xA0; &quot;Products&quot;: {&#xA;&#xA0; &#xA0; &#xA0; &#xA0; &quot;Umbraco.Forms&quot;: &quot;UMBRACO-CLOUD&quot;,&#xA;&#xA0; &#xA0; &#xA0; &#xA0; &quot;Umbraco.Deploy&quot;: &quot;UMBRACO-CLOUD&quot;&#xA;&#xA0; &#xA0; &#xA0; }&#xA;&#xA0; &#xA0; }&#xA;&#xA0; }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;Build, Run, and Test!&lt;/h2&gt;&#xA;&lt;p&gt;Is the site rendering as expected? Does the back-office load and you are able to login? If yes, you&#x27;re on your way to v17.&lt;/p&gt;&#xA;&lt;p&gt;Now its time to perform your detailed regression testing by verifying all the packages migrated as expected, all property editors are working as expected, and you can make changes as you would expect.&lt;/p&gt;&#xA;&lt;p&gt;Here are some items you may want to focus on to verify the upgrade:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The site builds without errors&lt;/li&gt;&#xA;&lt;li&gt;All pages render correctly&lt;/li&gt;&#xA;&lt;li&gt;Backoffice login works&lt;/li&gt;&#xA;&lt;li&gt;Content editing works&lt;/li&gt;&#xA;&lt;li&gt;No console errors/warnings&lt;/li&gt;&#xA;&lt;li&gt;Block List editors function&lt;/li&gt;&#xA;&lt;li&gt;Rich text editors function&lt;/li&gt;&#xA;&lt;li&gt;Media uploads work&lt;/li&gt;&#xA;&lt;li&gt;Search returns results&lt;/li&gt;&#xA;&lt;li&gt;Licenses show as valid&lt;/li&gt;&#xA;&lt;li&gt;Forms submit and save entries&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2&gt;Migrating Custom Extensions to v17&lt;/h2&gt;&#xA;&lt;div&gt;Once the back-office and site appear to be working, it&#x27;s time to migrate those custom extensions to v17. That process is out of scope for this article, but here are some resources to get you started:&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/extending/property-editors&quot; target=&quot;_blank&quot; title=&quot;Building Property Editors in v17&quot;&gt;Building Property Editors in v17&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/ui-library&quot; target=&quot;_blank&quot; title=&quot;Umbraco UI Library&quot;&gt;Umbraco UI Library&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://kjac.dev/posts/migrating-extensions-from-umbraco-13-to-17/&quot; target=&quot;_blank&quot; title=&quot;Migrating Extensions from v13 to v17&quot;&gt;Migrating Extensions from v13 to v17&lt;/a&gt; by Kenn Jacobsen&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://24days.in/umbraco-cms/2024/switching-to-web-components/)&quot; target=&quot;_blank&quot; title=&quot;From AngularJS to Web Components&quot;&gt;From AngularJS to Web Components&lt;/a&gt; by Jesper Mayntzhusen&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;Upgrading to v17 is worth the effort as it is the Umbraco version to take the community into the future. AI, modern web frameworks, enterprise features, and so much more make this an upgrade to prioritize. With v13 support ending December 14th, 2026 the time to start planning an upgrade is now.&lt;/p&gt;&#xA;&lt;h2&gt;Need help with your Umbraco upgrade?&lt;/h2&gt;&#xA;&lt;p&gt;Reach out to us for an initial consultation so we can help make your upgrade a success!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a style=&quot;color: #fff; background-image: none;&quot; type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;Schedule a Consultation&quot; class=&quot;pw-button&quot; data-anchor=&quot;#section-footer&quot;&gt;Schedule a Consultation&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&quot;change-log&quot;&gt;Change Log&lt;/h2&gt;&#xA;&lt;div&gt;2026-01-22: Initial Version&lt;/div&gt;]]></description>
                    <pubDate>Thu, 22 Jan 2026 10:20:17 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://fa9976c2-5840-4a63-82ec-42e17b070c7d-1.azurewebsites.net/blog/archive/upgrading-from-umbraco-13-to-17-lts-lessons-from-the-trenches/</guid>
                    <title>Upgrading from Umbraco 13 to 17 LTS: Lessons from the Trenches</title>
                    <link>https://fa9976c2-5840-4a63-82ec-42e17b070c7d-1.azurewebsites.net/blog/archive/upgrading-from-umbraco-13-to-17-lts-lessons-from-the-trenches/</link>
                    <description><![CDATA[&lt;p&gt;Upgrades, upgrades, upgrades!&#xA0; Upgrades are a hot topic in the Umbraco community ever since version 17 was released last November. There are a lot of great reasons to upgrade to this major version besides simply moving to the latest Long-Term Supported (LTS) version. Our team here at ProWorks has a ton of Umbraco upgrade experience and are here to help you make your upgrade a success. This article will update as we learn about new challenges and find solutions. Ready? Let&#x27;s do this!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a type=&quot;external&quot; href=&quot;#final-thoughts&quot; title=&quot;See the changelog at the bottom if you&#x27;re coming back to this after a while&quot; data-anchor=&quot;#final-thoughts&quot;&gt;See the changelog at the bottom if you&#x27;re coming back to this after a while&lt;/a&gt; - &lt;em&gt;Updated: January 22, 2026&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Why Upgrade?&lt;/h2&gt;&#xA;&lt;p&gt;Let&#x27;s look at why you would upgrade to the latest v17 version:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Timely Security Updates&lt;/strong&gt;&lt;/em&gt; - As a LTS version, your site can stay up to date with security patches and easiest to keep up to date in the future. Umbraco 17 is supported through November 27, 2028.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Better Site Performance&#xA0;&lt;/strong&gt;&lt;/em&gt;- The rebuilt HybridCache speeds up startups especially on large sites. This means less down time after deploys to live. The latest .NET version 10 framework also improves performance which helps with your page speeds.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Better Back-Office Performance&lt;/strong&gt;&lt;/em&gt; - The rebuilt back-office is faster and allows for standards-based extensions and future-proofed architecture that no longer relies on Angular or other external large frameworks that could no longer be supported.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Deep AI Chat Integrations&lt;/strong&gt;&lt;/em&gt; - The official Umbraco MCP Server only works on version 17 and allows AI to &quot;talk&quot; to your specific Umbraco back-office to report on, add, or modify content.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Better &lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;Automation and I&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;ntegration Support &lt;/strong&gt;&lt;/em&gt;- The new Management API allows for automation of everything that can be done by a content editor or developer in the back-office. This is important for deep integrations with external tools, marketing automations, and AI Agent connections.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;Scalable Back-office&lt;/strong&gt;&lt;/em&gt; - Version 17 supports load-balancing the back-office. This allows the back-office to scale out like the front-end for performance and simplifies hosting load-balanced Umbraco websites.&lt;/li&gt;&#xA;&lt;li&gt;Read even more on the official v17 release article: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://umbraco.com/blog/umbraco-17-lts-release/&quot; target=&quot;_blank&quot; title=&quot;Umbraco 17 LTS: Final Release&quot;&gt;Umbraco 17 LTS: Final Release&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;There is really a lot of truly great features available in version 17 and many more on the horizon in 18 and 19. It&#x27;s a perfect time to get this upgrade done so those other upgrades are less painful later.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;The sections below dive into the technical details of upgrading. If you&#x27;d rather have a conversation about what an upgrade means for your organization (timeline, budget, and business impact) we&#x27;re happy to help.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a style=&quot;color: #fff; background-image: none;&quot; type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;Request an Initial Consultation&quot; class=&quot;pw-button&quot; data-anchor=&quot;#section-footer&quot;&gt;Schedule a Consultation&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Prepare for Your Upgrade&lt;/h2&gt;&#xA;&lt;p&gt;A good result always starts with a good plan, right?&lt;/p&gt;&#xA;&lt;h4&gt;Current Umbraco Version?&lt;/h4&gt;&#xA;&lt;p&gt;First, you should assess where your site currently is. Are you on version 13? Or something older?&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 8-9:&lt;/strong&gt; You will need to upgrade to v10, then v13, then v17 - in fact, in general it is recommended to upgrade to each LTS version on your path to the latest. There may need to be custom migrations if the site uses Grid or other unsupported editors. Content migration may be preferable is some cases. v8 has API changes that could mean major code refactoring.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 10-12:&lt;/strong&gt; Upgrade to the latest v13, then to v17. Getting to v13 should be relatively straight-forward, but an assessment is always helpful for planning.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Version 13:&lt;/strong&gt; Upgrade to the latest version of v13, currently 13.13.0.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Learn more about each version here: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://releases.umbraco.com/all-releases&quot; target=&quot;_blank&quot; title=&quot;https://releases.umbraco.com/all-releases&quot;&gt;https://releases.umbraco.com/all-releases&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;(Please feel free to &lt;a type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;contact ProWorks&quot; data-anchor=&quot;#section-footer&quot;&gt;contact ProWorks&lt;/a&gt; and get our recommendations)&lt;/p&gt;&#xA;&lt;h4&gt;Using Outdated Property Editors?&lt;/h4&gt;&#xA;&lt;p&gt;If you are using any deprecated or legacy property editors, its best to update them to use the latest version in v13 before upgrading.&lt;/p&gt;&#xA;&lt;p&gt;Property editors to migrate to in v13:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;MediaPicker -&amp;gt; MediaPicker3&lt;/li&gt;&#xA;&lt;li&gt;Nested Content -&amp;gt; Block List&lt;/li&gt;&#xA;&lt;li&gt;Stacked Content -&amp;gt; Block List&lt;/li&gt;&#xA;&lt;li&gt;Grid -&amp;gt; Block Grid&lt;/li&gt;&#xA;&lt;li&gt;Macros -&amp;gt; Blocks (Or remove if unused)&lt;/li&gt;&#xA;&lt;li&gt;XPath in pickers -&amp;gt; Plan for a change to use the new dynamic root setup&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;By addressing these structural elements first, the content will migrate much easier to v17.&lt;/p&gt;&#xA;&lt;h4&gt;v17 Package Support?&lt;/h4&gt;&#xA;&lt;p&gt;Do you have Umbraco Packages installed (it&#x27;s likely)? Go to the Umbraco Marketplace (&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://marketplace.umbraco.com/&quot; target=&quot;_blank&quot; title=&quot;https://marketplace.umbraco.com/&quot;&gt;https://marketplace.umbraco.com/&lt;/a&gt;) or the package Repository and verify that each package supports v17. Many of the most used packages are ready for v17 now. Some are working on it and may have a beta version that likely works well. Some may not have any v17 support.&lt;/p&gt;&#xA;&lt;p&gt;If you&#x27;re unsure and its not clear from the repo, then asking on the Umbraco Forums (&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://forum.umbraco.com/&quot; target=&quot;_blank&quot; title=&quot;https://forum.umbraco.com/&quot;&gt;https://forum.umbraco.com/&lt;/a&gt;) would be a good way to see how others are dealing with this package. Is there a new version coming soon? Is there a new package that effectively replaces this one?&lt;/p&gt;&#xA;&lt;p&gt;As always,&#xA0;we can help give you a recommendation to move forward on. We have been doing this a long time and would love to help! &lt;a type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;Contact Us&quot; data-anchor=&quot;#section-footer&quot;&gt;Connect with us below&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;Custom/Bespoke Back-Office Extensions?&lt;/h4&gt;&#xA;&lt;p&gt;If your site has custom Angular.js extensions, then those will need to be re-written in the new Typescript/Lit/Web Components extension model. For the initial upgrade you can simply disable or remove them and address them after the main upgrade is complete.&lt;/p&gt;&#xA;&lt;h4&gt;Using Smidge?&lt;/h4&gt;&#xA;&lt;p&gt;Smidge was removed in v17 and you will need to reinstall it if used by any custom code or by the site in general. It is straight-forward to re-add to your site and you can learn more on the Github repo: &lt;a href=&quot;https://github.com/Shazwazza/Smidge&quot;&gt;https://github.com/Shazwazza/Smidge&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There is also an Umbraco-specific package that you can install here: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://marketplace.umbraco.com/package/umbraco.community.smidge&quot; target=&quot;_blank&quot; title=&quot;https://marketplace.umbraco.com/package/umbraco.community.smidge&quot;&gt;https://marketplace.umbraco.com/package/umbraco.community.smidge&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;TipTap or TinyMCE?&lt;/h4&gt;&#xA;&lt;p&gt;Umbraco has migrated to a new RTE component called TipTap. It will migrate all of your RTEs to this new editor by default unless you install a community package, &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://github.com/ProWorksCorporation/TinyMCE-Umbraco&quot; target=&quot;_blank&quot; title=&quot;TinyMCE.Umbraco Github Repository&quot;&gt;TinyMCE.Umbraco&lt;/a&gt;. This package will prevent the migration and keep the TinyMCE editor installed and used as in previous versions. &lt;a href=&quot;/blog/archive/tinymceumbraco-v17-released-and-ready-for-umbraco-17/&quot; title=&quot;TinyMCE.Umbraco v17 Released and Ready for Umbraco 17&quot;&gt;Read more about TinyMCE.Umbraco v17 in this post&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You will need to decide if you want to keep using TinyMCE or move to TipTap. If you have TinyMCE customizations or complex RTE setups, you may want to keep TinyMCE. The package also gives you the option of easily adding premium features like AI tools. TipTap is based on ProseMirror and is more lightweight so may be preferred in some use cases.&lt;/p&gt;&#xA;&lt;h4&gt;Use Umbraco Forms?&lt;/h4&gt;&#xA;&lt;p&gt;If you use Umbraco Forms, then you may need to migrate to the new licensing structure. It is now licensed annually instead of a one-time cost. Umbraco Forms is included with Umbraco Cloud so this is less of a concern if you are hosted on Umbraco Cloud. Your choice is to license Umbraco Forms going forward or remove it in favor of a different solution.&lt;/p&gt;&#xA;&lt;h4&gt;Review Official Documentation:&lt;/h4&gt;&#xA;&lt;p&gt;Take a look at the Version Specific Upgrades documentation guide in the Official Umbraco Documentation: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific&quot;&gt;Version Specific Upgrades&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;There may be other key breaking changes highlighted there that will be important for your site.&lt;/p&gt;&#xA;&lt;p&gt;Also, the Upgrade Details document has information on some edge cases and older versions as well: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/upgrade-details&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/upgrade-details&quot;&gt;Upgrade Details&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Pre-Upgrade Process&lt;/h2&gt;&#xA;&lt;p&gt;I&#x27;ll assume you have solutions for all the above issues and are ready to try out an upgrade! Great! Now, take a step to make sure you have all of this in place:&lt;/p&gt;&#xA;&lt;h4&gt;Backup Your Database / Media&lt;/h4&gt;&#xA;&lt;p&gt;Always backup your database before starting. You will miss something and may need to iterate a few times to get the migrations just right.&lt;/p&gt;&#xA;&lt;p&gt;Also, now is a good time to get a backup of the latest media and pull it locally.&lt;/p&gt;&#xA;&lt;h4&gt;Side-by-Side Setup&lt;/h4&gt;&#xA;&lt;p&gt;Ideally, you have a v13 site for reference running locally and your future v17 site ready to upgrade. These should be using separate databases and be able to run completely side-by-side. This lets you quickly iterate with v13 changes and re-run the v17 upgrade as needed. In addition, this allows you to quickly verify that the site looks and works as expected.&lt;/p&gt;&#xA;&lt;p&gt;If you&#x27;re using Git, make sure you have each version on separate feature branches.&lt;/p&gt;&#xA;&lt;h4&gt;Verify Dev Setup&lt;/h4&gt;&#xA;&lt;p&gt;Get your development setup ready for .NET 10 and Umbraco v17:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Install .NET 10 SDK&lt;/li&gt;&#xA;&lt;li&gt;Update Visual Studio (2022 v17.12&#x2B; or 2026)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4&gt;Verify Latest v13&lt;/h4&gt;&#xA;&lt;p&gt;One last check to make sure you&#x27;re on the latest Umbraco v13 release. If not, do this first.&lt;/p&gt;&#xA;&lt;h2&gt;Upgrade Time!&lt;/h2&gt;&#xA;&lt;p&gt;Let&#x27;s do this.&lt;/p&gt;&#xA;&lt;h4&gt;1. Update Project Target Frameworks&lt;/h4&gt;&#xA;&lt;p&gt;This can be done directly in the .csproj files through the TargetFramework element:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;    &amp;lt;!-- Change target framework --&amp;gt;&#xA;    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4&gt;2. Update Nuget Packages&lt;/h4&gt;&#xA;&lt;p&gt;Update all Umbraco packages to version 17.latest:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms.Web.Website&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If using Umbraco Cloud update these as well:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.StorageProviders.AzureBlob&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Deploy.Cloud&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Deploy.Contrib&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Remove these packages as they aren&#x27;t needed for v17:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Cms.PublicAccess&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cloud.Identity.Cms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Cms.Web.BackOffice&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Update all other packages used including Umbraco Extensions like:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Community.Contentment&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Umbraco.Forms&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Any other packages used that support v17&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4&gt;3. Delete Build Artifacts&lt;/h4&gt;&#xA;&lt;p&gt;Delete all of the&#xA0;&lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;obj&lt;/code&gt; directories in each of the projects of your solution. Umbraco has reported that Visual Studio&#x27;s &quot;Clean Solution&quot; option is sometimes not enough.&lt;/p&gt;&#xA;&lt;h4&gt;4. Code Changes&lt;/h4&gt;&#xA;&lt;p&gt;There are a few code changes needed to get the Solution compiling.&lt;/p&gt;&#xA;&lt;h5&gt;Program.cs:&lt;/h5&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Remove&#xA0;&lt;code&gt;UseInstallerEndpoints()&lt;/code&gt;&#xA0;as this is now automatically setup in v17&lt;/li&gt;&#xA;&lt;li&gt;Consider comparing a fresh installed Umbraco v17 &lt;strong&gt;Program.cs&lt;/strong&gt; with your current code and verify that you have the latest patterns in place. I have found other areas that were missing and easy to miss.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5&gt;_ViewImports.cshtml:&lt;/h5&gt;&#xA;&lt;div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Remove Smidge references:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/div&gt;&#xA;&lt;pre&gt;&lt;code&gt;@* REMOVE these lines: *@&#xA;@addTagHelper *, Smidge&#xA;@inject Smidge.SmidgeHelper SmidgeHelper&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h5&gt;Generated Models&lt;/h5&gt;&#xA;&lt;p&gt;If you used strongly typed models and have Umbraco generate them to code files in your project/solution, then the solution likely won&#x27;t compile.&lt;/p&gt;&#xA;&lt;p&gt;You have two options:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Delete all generated models from the directory in your project and set the &lt;code&gt;Umbraco.Cms.ModelsBuilder.ModelsMode&lt;/code&gt; to &quot;InMemoryAuto&quot; from &quot;SourceCodeAuto&quot; or &quot;SourceCodeManual&quot;. This will allow you to run the upgrade then switch it back and regenerate them after the v17 back-office is working. If you do this, you will need to install the &lt;code&gt;Umbraco.Cms.DevelopmentMode.Backoffice&lt;/code&gt; to allow the &quot;InMemoryAuto&quot; mode to work in v17.&lt;/li&gt;&#xA;&lt;li&gt;Tweak the v13 generated models so they compile and regenerate them after the v17 back-office is working. This involves:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Changing &lt;code&gt;IPublishedContent&lt;/code&gt; from:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;global::Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent Image { get; }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;To &lt;code&gt;MediaWithCrops&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;global::Umbraco.Cms.Core.Models.MediaWithCrops Image { get; }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Watch for &lt;code&gt;IEnumerable&lt;/code&gt; references as well as they need to be changed to use &lt;code&gt;MediaWithCrops&lt;/code&gt; as well. Then fix any other build errors that you see.&lt;/p&gt;&#xA;&lt;p&gt;Ultimately you want to run the back-office so you can regenerate them. So editing them manually is a temporary solution if you&#x27;re having trouble compiling the solution.&lt;/p&gt;&#xA;&lt;h4&gt;5. Configure Razor Files for .NET v10&lt;/h4&gt;&#xA;&lt;h5&gt;Verify Runtime Modes&lt;/h5&gt;&#xA;&lt;p&gt;.NET 10 deprecated runtime Razor compilation. Views are now compiled at build time by default.&lt;/p&gt;&#xA;&lt;p&gt;Verify your .csproj has the following:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;   &amp;lt;PropertyGroup&amp;gt;&#xA;      &amp;lt;CopyRazorGenerateFilesToPublishDirectory&amp;gt;true&amp;lt;/CopyRazorGenerateFilesToPublishDirectory&amp;gt;&#xA;   &amp;lt;/PropertyGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I recommend compiling your Razor files to find any issues at build time instead of at runtime. Remove these lines from your .csproj:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;      &amp;lt;RazorCompileOnBuild&amp;gt;false&amp;lt;/RazorCompileOnBuild&amp;gt;&#xA;      &amp;lt;RazorCompileOnPublish&amp;gt;false&amp;lt;/RazorCompileOnPublish&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;More on Runtime modes from the Umbraco Documentation: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/runtime-modes&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you need to restore the runtime Razor view compilation or want to use &lt;code&gt;InMemoryAuto&lt;/code&gt; model building, then you will need to install this package:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;dotnet add package Umbraco.Cms.DevelopmentMode.Backoffice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Read more in the Version Specific Guide to determine if you need this package: &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; data-anchor=&quot;#umbraco-17&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h4&gt;6. Optional: Disable Custom Packages&lt;/h4&gt;&#xA;&lt;p&gt;If you are planning to migrate custom back-office packages after the initial upgrade is complete, then you could simply disable any custom code and exclude it from the project.&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Rename folders in &lt;code&gt;App_Plugins&lt;/code&gt; with custom extensions to have &quot;.disabled&quot; at the end of the folder name.&lt;/li&gt;&#xA;&lt;li&gt;Update the &lt;code&gt;App_Plugins&lt;/code&gt; content include entry in the .csproj to exclude any folders with &quot;.disabled&quot; at the end:&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code&gt;      &amp;lt;Content Include=&quot;App_Plugins\**&quot; Exclude=&quot;App_Plugins\*.disabled\**&quot;&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If you have extensions added via Razor Class Library, you may need to remove the reference or delete the .dll from the &lt;code&gt;bin&lt;/code&gt; folder. &#xA0;&lt;/p&gt;&#xA;&lt;h4&gt;7. Optional: Explicitly Set LocalTimeZone&lt;/h4&gt;&#xA;&lt;p&gt;The change to use UTC by default for System Dates may have implications for your site. Read more in the Version Specific Guide on whether you want to explicitly set a server time zone in appsettings.json.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; target=&quot;_blank&quot; title=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&quot; data-anchor=&quot;#umbraco-17&quot;&gt;https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific#umbraco-17&lt;/a&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;{&#xA;   &quot;Umbraco&quot;: {&#xA;      &quot;CMS&quot;: {&#xA;         &quot;SystemDateMigration&quot;: {&#xA;            &quot;LocalServerTimeZone&quot;: &quot;Eastern Standard Time&quot;&#xA;         }&#xA;      }&#xA;   }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4&gt;8. Licensing Changes&lt;/h4&gt;&#xA;&lt;p&gt;If you are using Umbraco Deploy or Umbraco Forms, then you will need to migrate the licensing from the file-based .lic to the configuration settings in appsettings.json&lt;/p&gt;&#xA;&lt;h5&gt;Remove .lic Files&lt;/h5&gt;&#xA;&lt;p&gt;Remove the &lt;code&gt;.lic&lt;/code&gt; files inside of the &lt;code&gt;umbraco/Licenses&lt;/code&gt; folder for Umbraco Deploy and Umbraco Forms.&lt;/p&gt;&#xA;&lt;h5&gt;Add Licensing Configuration&lt;/h5&gt;&#xA;&lt;p&gt;For Umbraco Cloud sites, you can add these settings to your appsettings.json file. If you have a different hosting setup, you will need to replace &quot;UMBRACO-CLOUD&quot; with your license key.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;{&#xA;&#xA0; &quot;Umbraco&quot;: {&#xA;&#xA0; &#xA0; &quot;Licenses&quot;: {&#xA;&#xA0; &#xA0; &#xA0; &quot;Products&quot;: {&#xA;&#xA0; &#xA0; &#xA0; &#xA0; &quot;Umbraco.Forms&quot;: &quot;UMBRACO-CLOUD&quot;,&#xA;&#xA0; &#xA0; &#xA0; &#xA0; &quot;Umbraco.Deploy&quot;: &quot;UMBRACO-CLOUD&quot;&#xA;&#xA0; &#xA0; &#xA0; }&#xA;&#xA0; &#xA0; }&#xA;&#xA0; }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2&gt;Build, Run, and Test!&lt;/h2&gt;&#xA;&lt;p&gt;Is the site rendering as expected? Does the back-office load and you are able to login? If yes, you&#x27;re on your way to v17.&lt;/p&gt;&#xA;&lt;p&gt;Now its time to perform your detailed regression testing by verifying all the packages migrated as expected, all property editors are working as expected, and you can make changes as you would expect.&lt;/p&gt;&#xA;&lt;p&gt;Here are some items you may want to focus on to verify the upgrade:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The site builds without errors&lt;/li&gt;&#xA;&lt;li&gt;All pages render correctly&lt;/li&gt;&#xA;&lt;li&gt;Backoffice login works&lt;/li&gt;&#xA;&lt;li&gt;Content editing works&lt;/li&gt;&#xA;&lt;li&gt;No console errors/warnings&lt;/li&gt;&#xA;&lt;li&gt;Block List editors function&lt;/li&gt;&#xA;&lt;li&gt;Rich text editors function&lt;/li&gt;&#xA;&lt;li&gt;Media uploads work&lt;/li&gt;&#xA;&lt;li&gt;Search returns results&lt;/li&gt;&#xA;&lt;li&gt;Licenses show as valid&lt;/li&gt;&#xA;&lt;li&gt;Forms submit and save entries&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2&gt;Migrating Custom Extensions to v17&lt;/h2&gt;&#xA;&lt;div&gt;Once the back-office and site appear to be working, it&#x27;s time to migrate those custom extensions to v17. That process is out of scope for this article, but here are some resources to get you started:&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/extending/property-editors&quot; target=&quot;_blank&quot; title=&quot;Building Property Editors in v17&quot;&gt;Building Property Editors in v17&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/ui-library&quot; target=&quot;_blank&quot; title=&quot;Umbraco UI Library&quot;&gt;Umbraco UI Library&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://kjac.dev/posts/migrating-extensions-from-umbraco-13-to-17/&quot; target=&quot;_blank&quot; title=&quot;Migrating Extensions from v13 to v17&quot;&gt;Migrating Extensions from v13 to v17&lt;/a&gt; by Kenn Jacobsen&lt;/li&gt;&#xA;&lt;li&gt;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://24days.in/umbraco-cms/2024/switching-to-web-components/)&quot; target=&quot;_blank&quot; title=&quot;From AngularJS to Web Components&quot;&gt;From AngularJS to Web Components&lt;/a&gt; by Jesper Mayntzhusen&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;Upgrading to v17 is worth the effort as it is the Umbraco version to take the community into the future. AI, modern web frameworks, enterprise features, and so much more make this an upgrade to prioritize. With v13 support ending December 14th, 2026 the time to start planning an upgrade is now.&lt;/p&gt;&#xA;&lt;h2&gt;Need help with your Umbraco upgrade?&lt;/h2&gt;&#xA;&lt;p&gt;Reach out to us for an initial consultation so we can help make your upgrade a success!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a style=&quot;color: #fff; background-image: none;&quot; type=&quot;external&quot; href=&quot;#section-footer&quot; title=&quot;Schedule a Consultation&quot; class=&quot;pw-button&quot; data-anchor=&quot;#section-footer&quot;&gt;Schedule a Consultation&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&quot;change-log&quot;&gt;Change Log&lt;/h2&gt;&#xA;&lt;div&gt;2026-01-22: Initial Version&lt;/div&gt;]]></description>
                    <pubDate>Thu, 22 Jan 2026 10:20:17 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115932975121252464</guid>
                    <title>The Pre Spark Umbraco Hackathon and Package Jam is back!</title>
                    <link>https://umbracocommunity.social/@umbracospark/115932975121252464</link>
                    <description><![CDATA[&#x1F389; The Pre Spark Umbraco Hackathon and Package Jam is back! &#x1F389;Sign-ups are now open &#x1F449; https://www.meetup.com/umbristol/events/312954840If you&#x2019;re heading to #UmbracoSpark (tickets: https://www.tickettailor.com/events/umbukfdn/1896581) and taking part in the Package Jam, this is a great chance to collaborate in person as a team beforehand.#Umbraco #truedigital #umbristol #Bristol #TechCommunity]]></description>
                    <pubDate>Wed, 21 Jan 2026 12:11:39 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@UmbHost/115921099362344088</guid>
                    <title>UmbHost  GreenStack is now live!</title>
                    <link>https://umbracocommunity.social/@UmbHost/115921099362344088</link>
                    <description><![CDATA[&#x1F680; GreenStack is LIVE!After months of development, our load-balanced Umbraco hosting platform is here.&#x2705; Multi-replica infrastructure&#x2705; 99.9% uptime guarantee&#x2705; Zero-downtime deployments&#x2705; Powered by 100% renewable energyFrom &#xA3;20/mo https://umbhost.net/gb/blog/2026/01/greenstack-is-live-load-balanced-umbraco-hosting-is-here #GreenStack #Umbraco #WebHosting #SustainableHosting #CloudHosting #UmbHost]]></description>
                    <pubDate>Mon, 19 Jan 2026 09:43:34 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://dev.to/skttl/make-umbracos-welcome-dashboard-your-own-i1a</guid>
                    <title>Make Umbraco&#x2019;s Welcome Dashboard Your Own</title>
                    <link>https://dev.to/skttl/make-umbracos-welcome-dashboard-your-own-i1a</link>
                    <description><![CDATA[&lt;p&gt;Umbraco comes with a default Welcome dashboard in the backoffice. It displays news from Umbraco HQ along with handy links to documentation, training, and other community resources.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That&#x2019;s great for developers, but when you&#x2019;re building Umbraco sites for clients, those links are often not the most relevant. In fact, the traditional recommendation has often been to simply remove the Welcome dashboard entirely.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But with the current backoffice (which I should probably stop calling &lt;em&gt;new&lt;/em&gt; by now), there are plenty of extension points - and the Welcome dashboard is one of them.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So instead of removing it, why not use it for your own content?&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Extending the Welcome dashboard&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The Welcome dashboard is powered by a service in the backend, and you can replace or extend its behaviour by implementing the &lt;code&gt;INewsDashboardService&lt;/code&gt; interface in your own C# code.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Once you do that, Umbraco will use your implementation to populate the dashboard.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;All you need to return is a &lt;code&gt;NewsDashboardResponseModel&lt;/code&gt;, which contains a collection of items to display. Each item supports:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A header&lt;/li&gt;&#xA;&lt;li&gt;Body text&lt;/li&gt;&#xA;&lt;li&gt;An image (URL &#x2B; alt text)&lt;/li&gt;&#xA;&lt;li&gt;A link URL&lt;/li&gt;&#xA;&lt;li&gt;Button text&lt;/li&gt;&#xA;&lt;li&gt;A priority (High, Medium, or Low)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The dashboard will automatically sort the items by priority before rendering them.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A (slightly useless) example implementation&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;As a simple demonstration, here&#x2019;s an intentionally useless implementation that displays the weather from 12 random locations on Earth every time the dashboard loads.&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Api.Management.Services.NewsDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Api.Management.ViewModels.NewsDashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UselessNewsDashboardService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INewsDashboardService&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppCaches&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_appCaches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GiNewsDashboardService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppCaches&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appCaches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;_httpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;_appCaches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appCaches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewsDashboardResponseModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetItemsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// fetch some data - this depends on your usecase!&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locations&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_appCaches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RuntimeCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetCacheItemAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                &lt;span class=&quot;s&quot;&gt;&quot;randomWeather&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWeatherFromRandomLocationsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromHours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;c1&quot;&gt;// build the response model&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NewsDashboardResponseModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;Items&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NewsDashboardItemResponseModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;Priority&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;High&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// or Medium or Low&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShortDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;ButtonText&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Go to website&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// below are methods for communicating with the weather API &lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// - you don&#x27;t need these&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WeatherResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetWeatherAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// this is just to prevent 529 Too many requests&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&#xA;            &lt;span class=&quot;s&quot;&gt;$&quot;https://api.open-meteo.com/v1/forecast&quot;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;&#x2B;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;?latitude=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;longitude=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;&#x2B;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&amp;amp;current_weather=true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetStringAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RootElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;current_weather&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDouble&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weatherCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;weathercode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetInt32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weatherCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageUrl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weatherCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WeatherResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&#xB0;C&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;ShortDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Weather at &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;https://www.open-meteo.com/en/forecast?latitude=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;longitude=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WeatherResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ShortDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Clear sky&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Partly cloudy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;45&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;51&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;53&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;55&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Drizzle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;61&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rain&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;71&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;73&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;75&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Snow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;81&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;82&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rain showers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;95&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Thunderstorm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unknown weather&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetImageUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/869/869869.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/1163/1163661.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;61&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/3351/3351979.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;71&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;73&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;75&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/642/642102.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;m&quot;&gt;95&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/1146/1146869.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://cdn-icons-png.flaticon.com/512/1779/1779940.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;You also have to register the service with a Composer, like so:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Core.Composing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UselessNewsDashboardComposer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IComposer&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Compose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IUmbracoBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INewsDashboardService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UselessNewsDashboardComposer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;And there you have it:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frl5tzrs373it81y3c5pb.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frl5tzrs373it81y3c5pb.png&quot; alt=&quot;A useless news dashboard in Umbraco showing weather information from random places on Earth&quot; width=&quot;800&quot; height=&quot;841&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This example obviously isn&#x2019;t very useful - but it shows how little code is required to take control of the dashboard content.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A practical use case&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Where this does become useful is for agencies and teams building Umbraco sites for clients.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Instead of showing Umbraco HQ news, you can use the Welcome dashboard to:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Share important project updates&lt;/li&gt;&#xA;&lt;li&gt;Announce new features or releases&lt;/li&gt;&#xA;&lt;li&gt;Provide onboarding instructions&lt;/li&gt;&#xA;&lt;li&gt;Link to internal documentation or support channels&lt;/li&gt;&#xA;&lt;li&gt;Communicate maintenance windows or known issues&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;In other words, it becomes a direct communication channel inside your client&#x2019;s CMS.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Final thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Rather than removing the Welcome dashboard altogether, extending it allows you to turn an otherwise generic screen into something genuinely valuable for your clients.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With just a small custom service, you can make the Umbraco backoffice feel more tailored, more informative, and more aligned with the people actually using it.&lt;/p&gt;]]></description>
                    <pubDate>Mon, 19 Jan 2026 07:26:41 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/uprofile-january-2026-adam-prendergast/</guid>
                    <title>uProfile January 2026 - Adam Prendergast</title>
                    <link>https://umbraco.com/blog/uprofile-january-2026-adam-prendergast/</link>
                    <description><![CDATA[Meet Helen Chaplin &#x2014; a passionate sustainability advocate and avid world traveller]]></description>
                    <pubDate>Mon, 19 Jan 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@helloblake/115906539790690066</guid>
                    <title>Umbraco US Festival looking for community feedback</title>
                    <link>https://umbracocommunity.social/@helloblake/115906539790690066</link>
                    <description><![CDATA[&#x1F44B; #Umbraco community! We&#x2019;d love your input as we plan this year&#x2019;s Umbraco US Festival!What topics would you like to see discussed? Are there people (or you!) who would be great to speak on those subjects?We&#x2019;re also looking for folks who want to help behind the scenes, reviewing sessions, helping with comms, social posts, website updates, and more. All skill sets welcome!&#x1F4AC; Reply with ideas, tag someone, or reach out at events@umbracousfoundation.org to get involved.]]></description>
                    <pubDate>Sat, 17 Jan 2026 07:38:26 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/blendinteractive.umbraco.sitemap</guid>
                    <title>BlendInteractive.Umbraco.Sitemap</title>
                    <link>https://marketplace.umbraco.com/package/blendinteractive.umbraco.sitemap</link>
                    <description><![CDATA[sitemap.xml dynmically generated using the AppSettings and can be configured per environment.]]></description>
                    <pubDate>Sat, 17 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/djretailing.umbraco.dropdown</guid>
                    <title>DJRetailing.Umbraco.Dropdown</title>
                    <link>https://marketplace.umbraco.com/package/djretailing.umbraco.dropdown</link>
                    <description><![CDATA[Sleek dropdown suite for Umbraco. Verified stable for v14-v17. Supports C# Enums and Simple Dropdowns with full localization.]]></description>
                    <pubDate>Sat, 17 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/blendinteractive.umbraco.robotstxt</guid>
                    <title>BlendInteractive.Umbraco.RobotsTxt</title>
                    <link>https://marketplace.umbraco.com/package/blendinteractive.umbraco.robotstxt</link>
                    <description><![CDATA[robots.txt dynmically generated using the AppSettings and can be configured per environment.]]></description>
                    <pubDate>Sat, 17 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115899473100284278</guid>
                    <title>Umbraco Spark 2026 speaker line-up is live</title>
                    <link>https://umbracocommunity.social/@umbracospark/115899473100284278</link>
                    <description><![CDATA[&#x1F680; The Umbraco Spark 2026 speaker line-up is live!From Niels Lyngs&#xF8; on the backoffice, to Carl Sargunar&#x2019;s live load balancing, and Georgina &amp; Matt&#x2019;s AI-driven humor&#x2014;this year&#x2019;s Spark has something for every web professional.&#x1F39F;&#xFE0F; Tickets are limited! &#xA3;125 &#x2B; VAT until 31 Jan. Check out the full line-up &amp; get tickets https://umbracospark.com/ #UmbracoSpark #Umbraco #WebDev #TechEvents]]></description>
                    <pubDate>Thu, 15 Jan 2026 14:17:07 Z</pubDate>
                        <category>mastodon</category>
                        <category>social</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=CuBm41uczBM</guid>
                    <title>DevRel Deep Dive: Table Editing Community Packages</title>
                    <link>https://www.youtube.com/watch?v=CuBm41uczBM</link>
                    <description><![CDATA[This week Sebastiaan and Lotte take a look at two community packages that landed recently on the Umbraco marketplace, both adding the ability to allow editors to manage tabular data for properties: Webwonders &#x200B;Umbraco &#x200B;Table&#x200B;Editor and Umb&#x200B;Host &#x200B;Tables.&#xA;&#xA;&#x1F4E6; Webwonders &#x200B;Umbraco &#x200B;Table&#x200B;Editor on the Umbraco Marketplace: https://marketplace.umbraco.com/package/webwonders.umbraco.tableeditor&#xA;&#x1F4E6; Umb&#x200B;Host &#x200B;Tables on the Umbraco Marketplace: https://marketplace.umbraco.com/package/umbhost.tables&#xA;&#xA;--------&#xA;&#x2753; Looking for help with your Umbraco projects? Visit the Forum&#xA; https://forum.umbraco.com&#xA; &#xA;&#x1F4AC; Want to chat with the friendly Umbraco Community? Join our Discord server&#xA; https://discord.umbraco.com&#xA; &#xA;&#x2709;&#xFE0F; Got a question (or topic suggestion) for the HQ Developer Relations team?&#xA; https://umbra.co/contact-devrel, or the &#x2018;contact-devrel&#x2019; channel on Discord&#xA; &#xA;#Umbraco]]></description>
                    <pubDate>Wed, 14 Jan 2026 15:41:21 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115893344443045520</guid>
                    <title>Sponsor the Umbraco Spark Pre-Party in Bristol</title>
                    <link>https://umbracocommunity.social/@umbracospark/115893344443045520</link>
                    <description><![CDATA[&#x1F4A1; Want your brand at the heart of the Umbraco community?Sponsor the Umbraco Spark Pre-Party in Bristol @ Roxy Lanes, Bristol &#x1F37B;&#x2714;&#xFE0F; Sole sponsor&#x2714;&#xFE0F; 2 free Spark 2026 tickets&#x2714;&#xFE0F; Food, drinks &amp; good vibes for the community #UmbracoSpark #Umbraco #BristolTech #WebDev #DotNet]]></description>
                    <pubDate>Wed, 14 Jan 2026 14:55:16 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://dev.to/mathilde_hoest/what-working-in-forensic-psychiatry-has-taught-me-about-the-umbraco-community-54lj</guid>
                    <title>What Working in Forensic Psychiatry Has Taught Me About the Umbraco Community - DEV Community</title>
                    <link>https://dev.to/mathilde_hoest/what-working-in-forensic-psychiatry-has-taught-me-about-the-umbraco-community-54lj</link>
                    <description><![CDATA[What Working in Forensic Psychiatry Has Taught Me About the Umbraco Community - DEV Community]]></description>
                    <pubDate>Wed, 14 Jan 2026 09:13:40 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nathanielnunes.com/blog/using-umbraco-media-picker-crops-with-nextjs</guid>
                    <title>Using Umbraco Media Picker Crops with Next.js</title>
                    <link>https://www.nathanielnunes.com/blog/using-umbraco-media-picker-crops-with-nextjs</link>
                    <description><![CDATA[https://www.nathanielnunes.com/blog/using-umbraco-media-picker-crops-with-nextjs&#xA;&#xA;@UMB.FYI#3772 tip]]></description>
                    <pubDate>Tue, 13 Jan 2026 11:00:28 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.extensions.polyfills</guid>
                    <title>Our.Umbraco.Extensions.Polyfills</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.extensions.polyfills</link>
                    <description><![CDATA[Polyfills for broader compatibility in Umbraco]]></description>
                    <pubDate>Tue, 13 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/perplex.contentblockstoblocklist</guid>
                    <title>Perplex.ContentBlocksToBlockList</title>
                    <link>https://marketplace.umbraco.com/package/perplex.contentblockstoblocklist</link>
                    <description><![CDATA[Migrates Perplex.ContentBlocks data types and property data to Umbraco.BlockList]]></description>
                    <pubDate>Tue, 13 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.extensions.localization</guid>
                    <title>Our.Umbraco.Extensions.Localization</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.extensions.localization</link>
                    <description><![CDATA[Extensions for improved localization in Umbraco]]></description>
                    <pubDate>Tue, 13 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.extensions.blocks</guid>
                    <title>Our.Umbraco.Extensions.Blocks</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.extensions.blocks</link>
                    <description><![CDATA[Extensions for working with Blocks in Umbraco]]></description>
                    <pubDate>Tue, 13 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/developerlee_some-of-you-might-remember-merchello-the-ugcPost-7415400419331637248-baMp/</guid>
                    <title>Merchello, the community ecommerce package for Umbraco is making a come back</title>
                    <link>https://www.linkedin.com/posts/developerlee_some-of-you-might-remember-merchello-the-ugcPost-7415400419331637248-baMp/</link>
                    <description><![CDATA[Merchello]]></description>
                    <pubDate>Mon, 12 Jan 2026 10:31:32 Z</pubDate>
                        <category>linkedin</category>
                        <category>community</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.johanreitsma.com/blogs/upgrading-from-umbraco-13-to-17-my-practical-approach/</guid>
                    <title>Upgrading from Umbraco 13 to 17: My Practical Approach</title>
                    <link>https://www.johanreitsma.com/blogs/upgrading-from-umbraco-13-to-17-my-practical-approach/</link>
                    <description><![CDATA[...]]></description>
                    <pubDate>Mon, 12 Jan 2026 09:07:29 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://cultiv.social/@sebastiaan/115865097803711443</guid>
                    <title>DevRel Deep Dives are back!</title>
                    <link>https://cultiv.social/@sebastiaan/115865097803711443</link>
                    <description><![CDATA[Yes, DevRel Deep Dives are back after a bit of a longer break.In this episode @lotte and I have special guest @leekelleher in to show us around some new goodies in yesterday&#x27;s Umbraco 17.1.0 release. https://www.youtube.com/watch?v=9PtP2qIfYM0#Umbraco]]></description>
                    <pubDate>Sun, 11 Jan 2026 11:44:18 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://medium.com/@sornphut/anthem-tryhackme-walkthrough-37ef5d28f9d1?source=rss------umbraco-5</guid>
                    <title>Anthem | TryHackMe | Walkthrough</title>
                    <link>https://medium.com/@sornphut/anthem-tryhackme-walkthrough-37ef5d28f9d1?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@sornphut/anthem-tryhackme-walkthrough-37ef5d28f9d1?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/600/0*3vh2w4xfoKc9PXfJ.gif&quot; width=&quot;600&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Initial Access&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@sornphut/anthem-tryhackme-walkthrough-37ef5d28f9d1?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Sat, 10 Jan 2026 17:13:35 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=9PtP2qIfYM0</guid>
                    <title>Umbraco 17.1 highlights with Lee Kelleher</title>
                    <link>https://www.youtube.com/watch?v=9PtP2qIfYM0</link>
                    <description><![CDATA[Sebastiaan and Lotte look at some highlights of the latest Umbraco release - version 17.1 - with the help of Lee Kelleher from the Umbraco CMS team. Umbraco 17.1 brings a lot of accessibility and UX improvements, bug fixes and other enhancements. Learn about Block Sort Mode, configuring Additional Preview Environments for headless implementations, and more!&#xA;&#xA;&#x1F440; Check out the 17.1 release notes: https://releases.umbraco.com/release/umbraco/Umbraco-CMS/17.1.0&#xA;&#xA;&#x1F4D5;Additional preview environments support: https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api/additional-preview-environments-support &#xA;&#xA;--------&#xA;&#x2753; Looking for help with your Umbraco projects? Visit the Forum&#xA; https://forum.umbraco.com&#xA; &#xA;&#x1F4AC; Want to chat with the friendly Umbraco Community? Join our Discord server&#xA; https://discord.umbraco.com&#xA; &#xA;&#x2709;&#xFE0F; Got a question (or topic suggestion) for the HQ Developer Relations team?&#xA; https://umbra.co/contact-devrel, or the &#x2018;contact-devrel&#x2019; channel on Discord&#xA; &#xA;#Umbraco]]></description>
                    <pubDate>Fri, 09 Jan 2026 10:46:25 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://dev.to/skttl/readable-simple-chinese-url-segments-in-umbraco-with-npinyin-ajp</guid>
                    <title>Readable, simple Chinese URL segments in Umbraco with NPinyin</title>
                    <link>https://dev.to/skttl/readable-simple-chinese-url-segments-in-umbraco-with-npinyin-ajp</link>
                    <description><![CDATA[&lt;p&gt;On a recent project, the client had a Chinese version of their site. The texts and titles were, of course, written using Chinese characters, which meant that the URLs were automatically generated using those characters as well.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For SEO and UX reasons, we wanted the URLs to use Latin characters instead. While search engines are perfectly capable of indexing Chinese URLs, we felt that Latin URLs would perform better in practice.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Our reasoning was:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;Latin URLs are more readable (especially since the audience was not exclusively Chinese), which we assumed would lead to higher click-through rates&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;Hyphenated Latin words provide clearer URL keyword signals&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;Sharing URLs and linking to them is much easier with Latin characters than with encoded Chinese characters&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;For example, a title like:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&#x51FB;&#x638C;&#xFF01;&#x4F60;&#x592A;&#x68D2;&#x4E86;&#xFF01;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;would normally generate a URL segment using Chinese characters. With pinyin conversion applied, it instead becomes:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;/ji-zhang-ni-tai-bang-le&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The latter is much easier to read, share, and reason about, especially in mixed-language environments.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Converting Chinese characters to Latin using pinyin&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Umbraco already tries to convert special characters when generating URLs, but the default character replacement set is quite limited. You can see the default configuration in the request handler settings in the documentation article about &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/configuration/requesthandlersettings&quot; rel=&quot;noopener noreferrer&quot;&gt;Request Handler Settings&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Because of the nature of Chinese characters, having more than 50,000 of them, mapping each character to a Latin equivalent is not feasible. Instead, we can use pinyin, which is a Latin-based phonetic transcription system for Chinese characters.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Using pinyin allows us to convert Chinese text into readable Latin equivalents without maintaining huge character maps. There is an open-source NuGet package called &lt;a href=&quot;https://www.nuget.org/packages/NPinyin.Core&quot; rel=&quot;noopener noreferrer&quot;&gt;NPinyin.Core&lt;/a&gt; that makes this conversion straightforward. It&#x2019;s lightweight and integrates cleanly into existing logic.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This approach also works well for mixed-language titles, such as:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&#x51FB;&#x638C;&#xFF01;You Rock!&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;which results in:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;/ji-zhang-you-rock&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Using a custom URL segment provider in Umbraco&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With the conversion logic in place, the next step was to make Umbraco use it when generating URL segments.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When Umbraco generates URL segments, it runs through a set of URL segment providers, and it&#x2019;s possible to create a custom one. The process is simple and &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/routing/request-pipeline/outbound-pipeline#url-segment-provider&quot; rel=&quot;noopener noreferrer&quot;&gt;well documented&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;By adding a small amount of additional logic to separate Chinese characters with dashes, we ended up with a short and simple custom segment provider that generates readable, SEO-friendly Latin URLs for Chinese pages.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;** Note: **&lt;br&gt;&#xA;This approach converts each Chinese character to its pinyin representation and inserts dashes between characters. While this is not full linguistic word segmentation, it produces consistent, readable URL segments without adding unnecessary complexity.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Example implementation&#xA;&lt;/h2&gt;&#xA;&#xA;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;NPinyin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Core.Models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Umbraco.Cms.Core.Strings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Project.Core.Urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;&lt;span class=&quot;c1&quot;&gt;// Custom URL segment provider that wraps Umbraco&#x27;s default provider&lt;/span&gt;&#xA;&lt;span class=&quot;c1&quot;&gt;// and applies Chinese to Pinyin conversion on top.&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ChineseUrlSegmentProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IUrlSegmentProvider&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// We delegate to the default provider first, so we keep&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Umbraco&#x27;s built-in behavior for non-Chinese characters.&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DefaultUrlSegmentProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_defaultUrlSegmentProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChineseUrlSegmentProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IShortStringHelper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shortStringHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;n&quot;&gt;_defaultUrlSegmentProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;DefaultUrlSegmentProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shortStringHelper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetUrlSegment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IContentBase&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// Let Umbraco generate the initial URL segment,&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&quot;&gt;// then post-process it with our custom logic.&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChineseToPinyinWithDashes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;_defaultUrlSegmentProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetUrlSegment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;culture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Converts Chinese characters to pinyin and inserts dashes&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// between consecutive Chinese characters to improve readability.&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ChineseToPinyinWithDashes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastWasChinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsChinese&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;c1&quot;&gt;// Insert a dash between consecutive Chinese characters&lt;/span&gt;&#xA;                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastWasChinese&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                    &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&#x27;-&#x27;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;                &lt;span class=&quot;c1&quot;&gt;// Convert the individual character to pinyin&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pinyin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetPinyin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()));&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;lastWasChinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;                &lt;span class=&quot;c1&quot;&gt;// Non-Chinese characters are passed through unchanged&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;                &lt;span class=&quot;n&quot;&gt;lastWasChinese&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// Simple Unicode range check for CJK Unified Ideographs&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IsChinese&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x4E00&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0x9FFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Results&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With this approach in place, Chinese pages now automatically generate clean, readable, and shareable URL segments without any manual intervention. It works equally well for fully Chinese titles and mixed-language content, and it integrates cleanly into Umbraco&#x2019;s existing routing pipeline.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The solution is lightweight, easy to maintain, and avoids the complexity of large character-mapping tables or full linguistic word segmentation &#x2014; making it a practical choice for real-world Umbraco projects.&lt;/p&gt;]]></description>
                    <pubDate>Fri, 09 Jan 2026 08:29:17 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/fastgooey</guid>
                    <title>FastGooey</title>
                    <link>https://marketplace.umbraco.com/package/fastgooey</link>
                    <description><![CDATA[Render content into native iOS and macOS UI directly from your Umbraco projects.]]></description>
                    <pubDate>Fri, 09 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/phil-whittaker-82481716_umbraco-ai-mcp-activity-7415068292215930880-0KRg?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</guid>
                    <title>Umbraco CMS Developer MCP v17.0.1-beta.3 is out!</title>
                    <link>https://www.linkedin.com/posts/phil-whittaker-82481716_umbraco-ai-mcp-activity-7415068292215930880-0KRg?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</link>
                    <description><![CDATA[https://www.linkedin.com/posts/phil-whittaker-82481716_umbraco-ai-mcp-activity-7415068292215930880-0KRg?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw]]></description>
                    <pubDate>Thu, 08 Jan 2026 17:51:47 Z</pubDate>
                        <category>community</category>
                        <category>linkedin</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://mastodon.social/@deanleigh/115859499427123459</guid>
                    <title>UmBootstrap-17.1.0 released to match the release of Umbraco-17.1.0</title>
                    <link>https://mastodon.social/@deanleigh/115859499427123459</link>
                    <description><![CDATA[I have just released #UmBootstrap-17.1.0 to match the release of #Umbraco-17.1.0.It includes a some new features, content and bugfixeshttps://umbootstrap.com/]]></description>
                    <pubDate>Thu, 08 Jan 2026 13:27:19 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115853702848510381</guid>
                    <title>The Call for Speakers for Umbraco Spark is now closed!</title>
                    <link>https://umbracocommunity.social/@umbracospark/115853702848510381</link>
                    <description><![CDATA[&#x1F3A4; The Call for Speakers for Umbraco Spark is now closed!Thank you to everyone who submitted &#x2014; we&#x2019;re now reviewing all proposals.Speaker line up coming soon &#x1F440;&#x1F449; https://umbracospark.com/#UmbracoSpark #CFP #Umbraco]]></description>
                    <pubDate>Thu, 08 Jan 2026 12:02:37 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115859367848729995</guid>
                    <title>Busra Sengul from true digital will be one of the MCs at Umbraco Spark 2026</title>
                    <link>https://umbracocommunity.social/@umbracospark/115859367848729995</link>
                    <description><![CDATA[Announcement time! &#x1F680; Busra Sengul from true digital will be one of the MCs at Umbraco Spark 2026!Speaker lineup coming soon &#x2014; stay tuned &#x1F440; &#x1F449; https://umbracospark.com/#UmbracoSpark #truedigital #Umbraco]]></description>
                    <pubDate>Thu, 08 Jan 2026 12:02:24 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/new-year-new-perspectives-the-importance-of-togetherness/</guid>
                    <title>New Year, New Perspectives: The Importance of Togetherness</title>
                    <link>https://umbraco.com/blog/new-year-new-perspectives-the-importance-of-togetherness/</link>
                    <description><![CDATA[A new year has a funny way of inviting reflection. December often finds us looking back at the path we&#x2019;ve taken, evaluating what worked, what didn&#x27;t, and what we hope looks different twelve months from now.]]></description>
                    <pubDate>Thu, 08 Jan 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rewdboy.umbraco.editlink</guid>
                    <title>Rewdboy.Umbraco.EditLink</title>
                    <link>https://marketplace.umbraco.com/package/rewdboy.umbraco.editlink</link>
                    <description><![CDATA[Package Description]]></description>
                    <pubDate>Thu, 08 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=7a7U0iRIu48</guid>
                    <title>Mastering the Subtle Art of MCP</title>
                    <link>https://www.youtube.com/watch?v=7a7U0iRIu48</link>
                    <description><![CDATA[MCP is rapidly becoming a key layer in how AI systems connect with the tools and services developers rely on. Its real strength shows up when you go past simple integrations and start building deeper, structured capabilities.&#xA;&#xA;This session features Phil sharing practical guidance for creating reliable, production-ready MCP servers, drawing on insights from the Umbraco MCP implementation now adopted by the wider community.]]></description>
                    <pubDate>Wed, 07 Jan 2026 05:18:35 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://medium.com/@procustomsoftware/top-reasons-businesses-choose-to-hire-remote-umbraco-developers-aa6a9f687d4a?source=rss------umbraco-5</guid>
                    <title>Top Reasons Businesses Choose to Hire Remote Umbraco Developers</title>
                    <link>https://medium.com/@procustomsoftware/top-reasons-businesses-choose-to-hire-remote-umbraco-developers-aa6a9f687d4a?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/top-reasons-businesses-choose-to-hire-remote-umbraco-developers-aa6a9f687d4a?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*NOAohetec0Ll6DIq7mDbsQ.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;In today&amp;#x2019;s competitive digital landscape, businesses need scalable, secure, and flexible content management systems to stay ahead. Umbraco&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/top-reasons-businesses-choose-to-hire-remote-umbraco-developers-aa6a9f687d4a?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Tue, 06 Jan 2026 11:12:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#123</guid>
                    <title>Skrift Issue #123</title>
                    <link>https://skrift.io/issues/#123</link>
                    <description><![CDATA[Featuring guest posts by Bishal Tim on &quot;Fully Automating Umbraco v17 Backoffice with AI &#x2B; Umbraco MCP&quot; and Shelly Chauhan on &quot;Setting Up Umbraco With Umbraco Commerce&quot;]]></description>
                    <pubDate>Tue, 06 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.prima</guid>
                    <title>uSkinnedSiteBuilder.StarterKit.Prima</title>
                    <link>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.prima</link>
                    <description><![CDATA[Responsive Starter Kit &amp; Theme design add-on for uSkinned Site Builder demonstrating a fictional website for a Pizza Restaurant.]]></description>
                    <pubDate>Tue, 06 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/webwonders.umbraco.tableeditor</guid>
                    <title>Webwonders.Umbraco.TableEditor</title>
                    <link>https://marketplace.umbraco.com/package/webwonders.umbraco.tableeditor</link>
                    <description><![CDATA[A modern and flexible table editor for Umbraco.&#xA;    Provides editors with fine-grained control over table structure and semantics,&#xA;    while giving developers a clean and extensible rendering experience.]]></description>
                    <pubDate>Tue, 06 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/foxcart.payments.invoice</guid>
                    <title>FoxCart.Payments.Invoice</title>
                    <link>https://marketplace.umbraco.com/package/foxcart.payments.invoice</link>
                    <description><![CDATA[FoxCart e-commerce platform - Invoice/offline payment provider for manual payment processing.]]></description>
                    <pubDate>Mon, 05 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/foxcart.core</guid>
                    <title>FoxCart.Core</title>
                    <link>https://marketplace.umbraco.com/package/foxcart.core</link>
                    <description><![CDATA[FoxCart e-commerce platform - Core business logic, services, payment/shipping providers, and licensing for Umbraco CMS. Requires valid license for production use.]]></description>
                    <pubDate>Mon, 05 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/foxcart.payments.mollie</guid>
                    <title>FoxCart.Payments.Mollie</title>
                    <link>https://marketplace.umbraco.com/package/foxcart.payments.mollie</link>
                    <description><![CDATA[FoxCart e-commerce platform - Mollie payment provider supporting iDEAL, Bancontact, credit cards, and more.]]></description>
                    <pubDate>Mon, 05 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/foxcart.views</guid>
                    <title>FoxCart.Views</title>
                    <link>https://marketplace.umbraco.com/package/foxcart.views</link>
                    <description><![CDATA[FoxCart e-commerce platform - Shared Razor views, DTOs, and static assets for Umbraco CMS.]]></description>
                    <pubDate>Mon, 05 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/foxcart.payments.stripe</guid>
                    <title>FoxCart.Payments.Stripe</title>
                    <link>https://marketplace.umbraco.com/package/foxcart.payments.stripe</link>
                    <description><![CDATA[FoxCart e-commerce platform - Stripe payment provider supporting credit cards, Apple Pay, Google Pay, and more.]]></description>
                    <pubDate>Mon, 05 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.camper</guid>
                    <title>uSkinnedSiteBuilder.StarterKit.Camper</title>
                    <link>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.camper</link>
                    <description><![CDATA[Responsive Starter Kit &amp; Theme design add-on for uSkinned Site Builder demonstrating a fictional Camping and Caravan Site website.]]></description>
                    <pubDate>Sun, 04 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/owain.codes/post/3mbk4pdr2k22p</guid>
                    <title>OC.MediaColourFinderV2 is now available</title>
                    <link>https://bsky.app/profile/owain.codes/post/3mbk4pdr2k22p</link>
                    <description><![CDATA[OC.MediaColourFinderV2 is now available for #umbraco &#xA;&#xA;github.com/OwainWilliam...&#xA;&#xA;www.nuget.org/packages/OC....&#xA;&#xA;]]></description>
                    <pubDate>Sat, 03 Jan 2026 19:17:17 Z</pubDate>
                        <category>bluesky</category>
                        <category>community</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.mediacolourfinderv2</guid>
                    <title>OC.MediaColourFinderV2</title>
                    <link>https://marketplace.umbraco.com/package/oc.mediacolourfinderv2</link>
                    <description><![CDATA[This package enables you to obtain an images brightest color, average color, and opposite color to the brightest which can then be used in your designs]]></description>
                    <pubDate>Sat, 03 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@aaronsadleruk/115826779157157823</guid>
                    <title>UmbNav 4.0.2 for Umbraco 17 is out</title>
                    <link>https://umbracocommunity.social/@aaronsadleruk/115826779157157823</link>
                    <description><![CDATA[UmbNav 4.0.2 for #Umbraco 17 is out.This fixes an issue where child menu items would disappear when renaming a text parent itemGrab it here:https://marketplace.umbraco.com/package/umbraco.community.umbnav]]></description>
                    <pubDate>Fri, 02 Jan 2026 17:53:21 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=MV3xroHRHws</guid>
                    <title>Umbraco Community 2025 Highlights | Building, Learning, Growing</title>
                    <link>https://www.youtube.com/watch?v=MV3xroHRHws</link>
                    <description><![CDATA[A highlights recap of Umbraco community meetups from 2025, covering both online sessions and hybrid events.&#xA;Featuring key moments from talks, discussions, and community interactions across the Umbraco ecosystem&#x2014;presented as a visual montage with background music.&#xA;&#xA;Built by the community. Powered by shared learning.&#xA;&#xA;#Umbraco #UmbracoCommunity #UmbracoCMS #DotNet #TechMeetup #DeveloperCommunity]]></description>
                    <pubDate>Fri, 02 Jan 2026 04:19:55 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbhost.tables</guid>
                    <title>UmbHost.Tables</title>
                    <link>https://marketplace.umbraco.com/package/umbhost.tables</link>
                    <description><![CDATA[A table property editor for Umbraco 17 that allows content editors to create and manage tabular data with support for header rows/columns and rich text editing.]]></description>
                    <pubDate>Fri, 02 Jan 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115807982976624159</guid>
                    <title>Umbraco Spark 2026 Early Bird Tickets still available for &#xA3;100, but only until Midnight Dec 31st 2025</title>
                    <link>https://umbracocommunity.social/@umbraco/115807982976624159</link>
                    <description><![CDATA[&#x23F0; Early Bird reminder!Tickets for Umbraco Spark 2026 are available for &#xA3;100, but only until Dec 31 at midnight.&#x1F39F; Secure your place now: https://umbra.co/48PdFnj#UmbracoSpark #Umbraco #BristolTech #WebDev #DotNet]]></description>
                    <pubDate>Tue, 30 Dec 2025 11:08:40 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115797374209961397</guid>
                    <title>Umbraco events calendar launches on umbraco.com</title>
                    <link>https://umbracocommunity.social/@umbraco/115797374209961397</link>
                    <description><![CDATA[Finding the right Umbraco event just got a lot easier &#x1F440;We&#x2019;ve launched a brand new events calendar on umbraco.com, bringing all Umbraco events together in one place. From official Umbraco events to community driven meetups, you can now quickly see what&#x2019;s coming up (online or in person) &#x1F30D;Up next is our Winter Keynote on Feb 3. Join us for a front row seat to Umbraco&#x2019;s 2026 roadmap and strategy &#x2744;&#xFE0F;&#x1F525;Take a look and find your next Umbraco event at https://umbraco.com/events/#Umbraco]]></description>
                    <pubDate>Sun, 28 Dec 2025 13:46:23 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/forms-extensions/</guid>
                    <title>Umbraco Forms Extensions</title>
                    <link>https://24days.in/umbraco-cms/2025/forms-extensions/</link>
                    <description><![CDATA[Over a period of time, we&#x2019;ve been working on several client projects, and one of them came with a particularly interesting challenge: the need for extensive, custom form functionality. This gave us the perfect opportunity to explore how easy (or difficult) it actually is to extend Umbraco Forms.]]></description>
                    <pubDate>Thu, 25 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/mcp-usable-or-phase/</guid>
                    <title>Is Umbraco MCP Server Usable in Practice, or Is It Just a Phase?</title>
                    <link>https://24days.in/umbraco-cms/2025/mcp-usable-or-phase/</link>
                    <description><![CDATA[A non-developer&#x2019;s mildly sarcastic journey into prompt-driven CMS automation.]]></description>
                    <pubDate>Wed, 24 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@asha.chaudhary/custom-property-editors-in-umbraco-17-be7ec959119c?source=rss------umbraco-5</guid>
                    <title>Custom Property Editors in Umbraco 17</title>
                    <link>https://medium.com/@asha.chaudhary/custom-property-editors-in-umbraco-17-be7ec959119c?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@asha.chaudhary/custom-property-editors-in-umbraco-17-be7ec959119c?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*ekPL2ldgtiht-FzhUsYxCA.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;A deep technical guide to Umbraco 17 custom property editors covering architecture, data flow, validation, and enterprise best practices.&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@asha.chaudhary/custom-property-editors-in-umbraco-17-be7ec959119c?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Wed, 24 Dec 2025 06:53:37 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/skttl/say-hello-to-umux-sync-videos-from-umbraco-to-mux-effortlessly-1kng</guid>
                    <title>Say Hello to uMux: Sync videos from Umbraco to Mux effortlessly</title>
                    <link>https://dev.to/skttl/say-hello-to-umux-sync-videos-from-umbraco-to-mux-effortlessly-1kng</link>
                    <description><![CDATA[&lt;p&gt;uMux is a brand new, free and open source, package for Umbraco 17&#x2B;, that takes the hassle out of hosting videos on your website. Just drop your videos into Umbraco, and uMux will automatically upload and sync them with Mux. That means you get all the power of Mux&#x2019;s streaming, thumbnails, and analytics, while still working in the CMS you love.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why uMux?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For years, I found myself recommending platforms like Vimeo or similar to clients because they&#x2019;re user-friendly and handle video streaming well. But there was always a catch: editors had to jump between systems, upload videos to Vimeo, then copy and paste IDs or embed codes back into Umbraco. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;It&#x2019;s clunky, error-prone, and just not the smooth experience editors deserve.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With uMux, my goal was to keep everything in one place.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Editors can upload and manage videos right inside Umbraco, just like they&#x2019;re used to, while still getting all the benefits of a modern video platform like Mux. No more juggling logins, no more copy-pasting codes, just a seamless workflow.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What does it do?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;uMux handles all the behind-the-scenes magic&#x2014;your videos get sent to Mux, and Mux handles encoding, so the videos are ready to stream anywhere. Editors don&#x2019;t have to worry about file types, encoding settings, or technical video stuff. It just works.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why use a third party video host?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Serving big video files from your own server can be a pain, especially if you&#x2019;re on Umbraco Cloud or have bandwidth limits. Here&#x2019;s why letting Mux handle your videos is a game-changer:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save your bandwidth&lt;/strong&gt;&lt;br&gt;&#xA;Mux takes care of the heavy lifting, so your Umbraco site stays speedy and you don&#x2019;t get hit with surprise bandwidth bills.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smooth streaming for everyone&lt;/strong&gt;&lt;br&gt;&#xA;Mux automatically streams the best quality for each viewer&#x2019;s device and connection, so videos load fast and play without hiccups.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global delivery&lt;/strong&gt;&lt;br&gt;&#xA;Your videos are delivered from servers close to your users, wherever they are in the world.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;In addition to those obvious benefits, Mux also gives you some handy features around the videos you host with them.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instant thumbnails &amp;amp; GIFs&lt;/strong&gt;&lt;br&gt;&#xA;Need a thumbnail or animated preview? Mux generates them for you&#x2014;no extra work needed.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Awesome analytics&lt;/strong&gt;&lt;br&gt;&#xA;See how your videos are performing and where viewers might be dropping off.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security &amp;amp; scalability&lt;/strong&gt;&lt;br&gt;&#xA;No worries about traffic spikes or unauthorized access&#x2014;Mux has you covered.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Works everywhere&lt;/strong&gt;&lt;br&gt;&#xA;Embed videos in any player that supports HLS, or use Mux&#x2019;s own slick player.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic encoding&lt;/strong&gt;&lt;br&gt;&#xA;Mux encodes your videos in all the right formats, so you don&#x2019;t have to think about codecs or compatibility.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Getting started&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Install uMux:&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;  dotnet add package Umbraco.Community.uMux&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;p&gt;Add your Mux credentials to your appsettings.json or as environment variables.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;Add the Mux Sync property editor to your media types in Umbraco.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;p&gt;Upload your videos as usual - uMux takes care of the rest!&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;For all the nitty-gritty details, check out the &lt;a href=&quot;https://github.com/skttl/umbraco-mux&quot; rel=&quot;noopener noreferrer&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;]]></description>
                    <pubDate>Tue, 23 Dec 2025 19:06:54 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/modular-umbraco/</guid>
                    <title>Modular Umbraco sites with NuGet, RCLs and uSync</title>
                    <link>https://24days.in/umbraco-cms/2025/modular-umbraco/</link>
                    <description><![CDATA[Many Umbraco teams have some kind of &#x201C;starter&#x201D; template for their projects - a collection of common content types or functionality, and helpers for things like SEO metadata. The goal is to give development a speed boost, and ultimately make projects more profitable.]]></description>
                    <pubDate>Tue, 23 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@girish.sasikumar.kl33/moving-an-existing-umbraco-solution-to-umbraco-cloud-cd77daf2a5e3?source=rss------umbraco-5</guid>
                    <title>Moving an Existing Umbraco Solution to Umbraco Cloud</title>
                    <link>https://medium.com/@girish.sasikumar.kl33/moving-an-existing-umbraco-solution-to-umbraco-cloud-cd77daf2a5e3?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@girish.sasikumar.kl33/moving-an-existing-umbraco-solution-to-umbraco-cloud-cd77daf2a5e3?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*fB3pGeN1Kfw5CWD1pXlJkA.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Migrating an already developed Umbraco solution&amp;#x200A;&amp;#x2014;&amp;#x200A;especially one with a custom architecture&amp;#x200A;&amp;#x2014;&amp;#x200A;to Umbraco Cloud can be challenging if not&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@girish.sasikumar.kl33/moving-an-existing-umbraco-solution-to-umbraco-cloud-cd77daf2a5e3?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Tue, 23 Dec 2025 06:32:26 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.umux</guid>
                    <title>Umbraco.Community.uMux</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.umux</link>
                    <description><![CDATA[Automatically sync local video assets from Umbraco to Mux]]></description>
                    <pubDate>Tue, 23 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.arroact.com/blogs/how-i-built-an-ai-content-generator-dashboard-in-umbraco-17/</guid>
                    <title>Building an AI Content Generator App Plugin for Umbraco 17</title>
                    <link>https://www.arroact.com/blogs/how-i-built-an-ai-content-generator-dashboard-in-umbraco-17/</link>
                    <description><![CDATA[Building an AI Content Generator App Plugin for Umbraco 17]]></description>
                    <pubDate>Mon, 22 Dec 2025 12:36:33 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.arroact.com/blogs/how-to-use-arro-simple-form-in-umbraco-13-to-17/</guid>
                    <title>How to Use Arro Simple Form in Umbraco 13&#x2013;17</title>
                    <link>https://www.arroact.com/blogs/how-to-use-arro-simple-form-in-umbraco-13-to-17/</link>
                    <description><![CDATA[How to Use Arro Simple Form in Umbraco 13&#x2013;17 | Dynamic No-Code Forms]]></description>
                    <pubDate>Mon, 22 Dec 2025 12:36:26 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://dev.to/addact_technologies_3322b/umbraco-vs-wordpress-which-cms-should-you-use-in-2026-23i5</guid>
                    <title>Umbraco vs WordPress: Which CMS Should You Use in 2026?</title>
                    <link>https://dev.to/addact_technologies_3322b/umbraco-vs-wordpress-which-cms-should-you-use-in-2026-23i5</link>
                    <description><![CDATA[&lt;p&gt;canonical_url:&lt;a href=&quot;https://www.addact.net/blogs/umbraco-vs-wordpress-which-cms-is-better&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.addact.net/blogs/umbraco-vs-wordpress-which-cms-is-better&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Choosing the right Content Management System (CMS) can make or break your next web project &#x2014; whether it&#x2019;s a blog, a corporate platform, or an enterprise portal. Two of the most widely discussed options are WordPress and Umbraco, but they serve distinctly different needs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this post, we&#x2019;ll break down their differences, strengths, and use cases so you can decide which one fits your project from a developer&#x2019;s perspective.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F680; What Are WordPress and Umbraco?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;The world&#x2019;s most popular CMS, powering ~40%&#x2B; of all websites on the internet.&lt;br&gt;&#xA;Written in PHP and built with simplicity and extensibility in mind.&lt;br&gt;&#xA;Designed to allow anyone to easily install a theme or plugin and launch a site quickly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;An open-source CMS built on Microsoft&#x2019;s .NET stack.&lt;br&gt;&#xA;Wikipedia&lt;br&gt;&#xA;Focuses on scalability, customization, and structured content.&lt;br&gt;&#xA;A favorite for enterprise-level applications and projects needing deep control.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F6E0;&#xFE0F; Customization &amp;amp; Flexibility&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;&#x26A1; Thousands of plugins and themes available&lt;br&gt;&#xA;&#x1F3A8; Rapid setup for common use cases&lt;br&gt;&#xA;&#x2757; Reliant on third-party plugins for extended functionality (which can raise maintenance overhead)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;&#x1F527; Built for developers &#x2014; blank canvas approach&lt;br&gt;&#xA;&#x1F4A1; Full control over architecture, workflows, and content models&lt;br&gt;&#xA;&#x1F4C8; Better suited for complex, bespoke systems&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#x1F449; Quick takeaway: WordPress gets you up and running fast; Umbraco lets you build exactly what you want at the cost of more upfront work.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x26A1; Performance &amp;amp; Scalability&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;Performance can vary depending on hosting, plugins, and themes.&lt;br&gt;&#xA;Scales decently with caching and optimizations.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;Built on compiled .NET code, which can deliver strong performance out of the box.&lt;br&gt;&#xA;Handles large, content-heavy, or high-traffic sites more predictably.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#x1F4A1; Developer angle: Umbraco&#x2019;s architecture often performs better in enterprise environments, while WordPress excels for small-to-mid projects with fewer technical constraints.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F6E1;&#xFE0F; Security Considerations&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;Massive ecosystem means massive visibility &#x2014; and more attack vectors.&lt;br&gt;&#xA;Security largely depends on plugins and update discipline.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;.NET foundation and enterprise-grade design yield fewer inherent vulnerabilities.&lt;br&gt;&#xA;A smaller install base means fewer automated attacks.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#x1F3AF; Bottom line: Out-of-the-box, Umbraco tends to be more secure &#x2014; but both platforms can be hardened with the right practices.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F4C8; SEO &amp;amp; Content Experience&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;&#x1F525; SEO is one of WordPress&#x2019;s biggest strengths &#x2014; plugins like Yoast and Rank Math make optimization very accessible.&lt;br&gt;&#xA;&#x1F4CA; Easy meta editing, permalinks, and content tagging.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;&#x1F50D; SEO is powerful but requires developer setup.&lt;br&gt;&#xA;&#x1F4CC; You build the SEO tools yourself or customize them deeply.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#x1F449; If you&#x2019;re delivering lots of editorial content with SEO requirements, WordPress is usually easier. For highly controlled or non-standard SEO scenarios, Umbraco gives you complete flexibility.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F4B0; Cost &amp;amp; Maintenance&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;Quick to set up and initially low cost&lt;br&gt;&#xA;Long-term maintenance (plugin updates &#x2B; security) can add complexity&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;Requires developer expertise and often higher initial cost&lt;br&gt;&#xA;More predictable long-term platform maintenance&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F465; Community &amp;amp; Ecosystem&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt;&lt;br&gt;&#xA;&#x1F30D; Huge community with tons of learning resources, plugins, and themes.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Umbraco&lt;/strong&gt;&lt;br&gt;&#xA;&#x1F468;&#x200D;&#x1F4BB; Smaller but dedicated developer community &#x2014; strong for .NET ecosystems.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  &#x1F9E9; Final Thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There isn&#x2019;t a &#x201C;one-size-fits-all&#x201D; answer.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go with WordPress if you want a widely supported, developer-friendly CMS with tons of ready-made extensions.&lt;/li&gt;&#xA;&lt;li&gt;Choose Umbraco for deep customization, enterprise stability, and .NET integration.&#xA;Both tools are open source and capable &#x2014; the right choice depends on your project&#x2019;s scope, team expertise, and long-term growth strategy.&lt;/li&gt;&#xA;&lt;/ul&gt;]]></description>
                    <pubDate>Mon, 22 Dec 2025 09:19:07 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/unlocking-accessibility/</guid>
                    <title>A Practical Guide to Unlocking Accessibility of Websites</title>
                    <link>https://24days.in/umbraco-cms/2025/unlocking-accessibility/</link>
                    <description><![CDATA[In this article, we dive into using powerful tools like WAVE to identify and address accessibility issues on your website. We also learn how to use these tools to spot common barriers, from missing alt text to improper heading structure, and take actionable steps to improve your site&#x2019;s accessibility.]]></description>
                    <pubDate>Mon, 22 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbracoform.mailchimp.workflow</guid>
                    <title>UmbracoForm.MailChimp.WorkFlow</title>
                    <link>https://marketplace.umbraco.com/package/umbracoform.mailchimp.workflow</link>
                    <description><![CDATA[Umbraco Forms V17 Mailchimp Integration WorkflowType]]></description>
                    <pubDate>Mon, 22 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/fluid-typography/</guid>
                    <title>An Introduction to Fluid Responsive Typography</title>
                    <link>https://24days.in/umbraco-cms/2025/fluid-typography/</link>
                    <description><![CDATA[With support for CSS math functions min(), max(), clamp(), calc(), Container Queries and Container  Query Units we have much greater control of typography.]]></description>
                    <pubDate>Sun, 21 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uthemes.flaticonpicker</guid>
                    <title>uThemes.FlaticonPicker</title>
                    <link>https://marketplace.umbraco.com/package/uthemes.flaticonpicker</link>
                    <description><![CDATA[Flaticon picker property editor for Umbraco CMS 17]]></description>
                    <pubDate>Sun, 21 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/commerce-lessons/</guid>
                    <title>Lessons learned on my first Umbraco Commerce project</title>
                    <link>https://24days.in/umbraco-cms/2025/commerce-lessons/</link>
                    <description><![CDATA[Building my first e-commerce solution with Umbraco Commerce has been a good learning opportunity.]]></description>
                    <pubDate>Sat, 20 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=9O2g1t5H6HQ</guid>
                    <title>umbraCoffee - Ho Ho Ho!</title>
                    <link>https://www.youtube.com/watch?v=9O2g1t5H6HQ</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;As is Christmas tradition, we are joined today by Umbraco Cloud product owner and all round wonderful human Rune Strand!&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;&#xA;// TODO]]></description>
                    <pubDate>Fri, 19 Dec 2025 09:45:09 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/christmas-crossword/</guid>
                    <title>Some (more) wintery wordplay!</title>
                    <link>https://24days.in/umbraco-cms/2025/christmas-crossword/</link>
                    <description><![CDATA[An Umbraco and technology themed cryptic crossword.]]></description>
                    <pubDate>Fri, 19 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/why-composability-gets-complicated/</guid>
                    <title>Why Composability Gets Complicated</title>
                    <link>https://umbraco.com/blog/why-composability-gets-complicated/</link>
                    <description><![CDATA[Get the key insights from our webinar with CMS Critic, focusing on why composability often becomes complex in practice and what teams tend to underestimate around data, architecture, and organisation.]]></description>
                    <pubDate>Fri, 19 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.cloud.healthchecks</guid>
                    <title>Umbraco.Community.Cloud.HealthChecks</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.cloud.healthchecks</link>
                    <description><![CDATA[A package for adding Umbraco Cloud health checks]]></description>
                    <pubDate>Fri, 19 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.enhancedmedia</guid>
                    <title>Umbraco.Community.EnhancedMedia</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.enhancedmedia</link>
                    <description><![CDATA[...]]></description>
                    <pubDate>Fri, 19 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.nathanielnunes.com/blog/altering-custom-table-columns-in-umbraco-migrations</guid>
                    <title>Altering Custom Table Columns in Umbraco Migrations</title>
                    <link>https://www.nathanielnunes.com/blog/altering-custom-table-columns-in-umbraco-migrations</link>
                    <description><![CDATA[https://www.nathanielnunes.com/blog/altering-custom-table-columns-in-umbraco-migrations&#xA;&#xA;@UMB.FYI#3772 tip]]></description>
                    <pubDate>Thu, 18 Dec 2025 12:36:08 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/sustainability-tips/</guid>
                    <title>10 tips to make your Umbraco site more sustainable</title>
                    <link>https://24days.in/umbraco-cms/2025/sustainability-tips/</link>
                    <description><![CDATA[Practical ways to make your site greener, with clear, simple tips you can start implementing today]]></description>
                    <pubDate>Thu, 18 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.1</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.1</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.1</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Wed, 17 Dec 2025 22:26:35 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracofreelancer.net/blog/keeping-claude-code-and-claude-desktop-in-sync-umbraco-freelancer/</guid>
                    <title>Keeping Claude Code and Claude Desktop in sync</title>
                    <link>https://umbracofreelancer.net/blog/keeping-claude-code-and-claude-desktop-in-sync-umbraco-freelancer/</link>
                    <description><![CDATA[https://umbracofreelancer.net/blog/keeping-claude-code-and-claude-desktop-in-sync-umbraco-freelancer/]]></description>
                    <pubDate>Wed, 17 Dec 2025 13:23:31 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/timeout-mystery/</guid>
                    <title>Fixing 42-Second API Delays: An IPv6 Timeout Mystery</title>
                    <link>https://24days.in/umbraco-cms/2025/timeout-mystery/</link>
                    <description><![CDATA[I spent days chasing a 42-second API delay that drove me crazy. It wasn&#x2019;t async/await, it was a broken IPv6 connection. Here&#x2019;s what I learned and how I fixed it]]></description>
                    <pubDate>Wed, 17 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://packages.limbo.works/skybrud.umbraco.redirects/releases/v17.0.1/</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.1</title>
                    <link>https://packages.limbo.works/skybrud.umbraco.redirects/releases/v17.0.1/</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Wed, 17 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/december/when-gitkraken-desktop-runs-slow/</guid>
                    <title>When GitKraken Desktop Runs Slow</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/december/when-gitkraken-desktop-runs-slow/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 17 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2025/december/when-gitkraken-desktop-runs-slow/</guid>
                    <title>When GitKraken Desktop Runs Slow</title>
                    <link>https://owain.codes/blog/2025/december/when-gitkraken-desktop-runs-slow/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 17 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@abanobemad435/authentication-in-umbraco-using-api-endpoints-jwt-f20b6ba993ff?source=rss------umbraco-5</guid>
                    <title>Authentication in Umbraco Using API Endpoints (JWT)</title>
                    <link>https://medium.com/@abanobemad435/authentication-in-umbraco-using-api-endpoints-jwt-f20b6ba993ff?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Introduction&amp;#xA0;: -&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@abanobemad435/authentication-in-umbraco-using-api-endpoints-jwt-f20b6ba993ff?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Tue, 16 Dec 2025 10:20:02 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/temporary-segments-engage/</guid>
                    <title>13 Smart Uses for Temporary Segments in Umbraco Engage on V13&#x2B;</title>
                    <link>https://24days.in/umbraco-cms/2025/temporary-segments-engage/</link>
                    <description><![CDATA[Martha and I, as marketers without technical backgrounds, attended our first Hackathon through the Umbraco Leeds Meet-up. The experience pushed us to apply our unique knowledge of Umbraco in creative ways, and inspired these practical tactics to help other Umbraco fans like us power up their personalisation and create campaigns that really resonate with Engage.]]></description>
                    <pubDate>Tue, 16 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbracocustomnavigation</guid>
                    <title>UmbracoCustomNavigation</title>
                    <link>https://marketplace.umbraco.com/package/umbracocustomnavigation</link>
                    <description><![CDATA[A reusable navigation component for umbraco]]></description>
                    <pubDate>Tue, 16 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/introducing-umbraco/</guid>
                    <title>Introducing Umbraco to a new team</title>
                    <link>https://24days.in/umbraco-cms/2025/introducing-umbraco/</link>
                    <description><![CDATA[A brief overview of how I introduced Umbraco to a team that had never used Umbraco, or any cloud-based CMS before.]]></description>
                    <pubDate>Mon, 15 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.hiddendashboard</guid>
                    <title>OC.HiddenDashboard</title>
                    <link>https://marketplace.umbraco.com/package/oc.hiddendashboard</link>
                    <description><![CDATA[A hidden dashboard for Umbraco 17, revealed by the Konami code (&#x2191; &#x2191; &#x2193; &#x2193; &#x2190; &#x2192;), with full extensibility for third-party packages to register their own hidden content.]]></description>
                    <pubDate>Mon, 15 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.oc.hiddendashboard</guid>
                    <title>Umbraco.Community.OC.HiddenDashboard</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.oc.hiddendashboard</link>
                    <description><![CDATA[Hide a dashboard with Konami code to unlock. Features extensibility for third-party packages, configuration options, and includes a playable Pac-Man game as an example.]]></description>
                    <pubDate>Mon, 15 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/december/ochiddendashboard-umbraco-package/</guid>
                    <title>OC.HiddenDashboard - Umbraco Package</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/december/ochiddendashboard-umbraco-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 15 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2025/december/ochiddendashboard-umbraco-package/</guid>
                    <title>OC.HiddenDashboard - Umbraco Package</title>
                    <link>https://owain.codes/blog/2025/december/ochiddendashboard-umbraco-package/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 15 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/idea-to-package/</guid>
                    <title>From Idea to Package: Building Member Impersonation for Umbraco backoffice</title>
                    <link>https://24days.in/umbraco-cms/2025/idea-to-package/</link>
                    <description><![CDATA[A beginner&#x2011;friendly introduction to building an Umbraco package. From server&#x2011;side impersonation to a entity action and a small UUI frontend.]]></description>
                    <pubDate>Sun, 14 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/template-for-success/</guid>
                    <title>Umbraco Flavored Markdown: A ${template} for success</title>
                    <link>https://24days.in/umbraco-cms/2025/template-for-success/</link>
                    <description><![CDATA[Editor experience isn&#x2019;t just a nice-to-have &#x2014; it&#x2019;s what makes or breaks your build. With the arrival of the first LTS using the new backoffice, we&#x27;re seeing fresh possibilities to shape more intuitive and delightful editing environments. But with great innovation (hello Bellissima!) comes the loss of old comforts (farewell AngularJS).]]></description>
                    <pubDate>Sat, 13 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/phases.umbraco.glossify</guid>
                    <title>Phases.Umbraco.Glossify</title>
                    <link>https://marketplace.umbraco.com/package/phases.umbraco.glossify</link>
                    <description><![CDATA[Glossify adds a glossary picker to the Umbraco RTE, allowing editors to link selected text to glossary nodes, highlight the linked content, and display definition tooltips on the front end.]]></description>
                    <pubDate>Sat, 13 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-for-enterprise/</guid>
                    <title>A New Level of Clarity and Confidence for Enterprise Projects</title>
                    <link>https://umbraco.com/blog/umbraco-for-enterprise/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Sat, 13 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://cultiv.social/@sebastiaan/115705645710422621</guid>
                    <title>Announcing Umbraco Power Tools, A small browser extension for Umbraco devs</title>
                    <link>https://cultiv.social/@sebastiaan/115705645710422621</link>
                    <description><![CDATA[It&#x27;s here: Umbraco Power Tools &#x1F9F0;A small browser extension for Umbraco devs:&#x2022; One-click switch between site &#x2194; /umbraco&#x2022; Quick links to forum, socials &amp; events&#x2022; Useful Umbraco resources in one placeFirefox: https://addons.mozilla.org/en-US/firefox/addon/umbraco-power-tools/Chrome &amp; Edge: https://chromewebstore.google.com/detail/umbraco-power-tools/mnnecgajjbmjjeneclnohbhaflmpjafh#Umbraco]]></description>
                    <pubDate>Fri, 12 Dec 2025 08:27:40 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/ddos-defence-guide/</guid>
                    <title>Keeping Your Website Off the Naughty List: A Practical Guide to DDoS Defense for Umbraco</title>
                    <link>https://24days.in/umbraco-cms/2025/ddos-defence-guide/</link>
                    <description><![CDATA[As the holiday season brings both happy traffic spikes and the occasional unwelcome visitor, it&#x2019;s the perfect time to make sure your website can handle the pressure. This guide walks you through real-world strategies for building DDoS resilience into your Umbraco (or any) site &#x2014; from understanding attack layers and optimizing caching to setting up smart firewall rules and throttling policies.]]></description>
                    <pubDate>Fri, 12 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.proworks.com/blog/archive/if-burnout-is-an-injury-passion-is-the-sport/</guid>
                    <title>If Burnout is an Injury, Passion is the Sport</title>
                    <link>https://www.proworks.com/blog/archive/if-burnout-is-an-injury-passion-is-the-sport/</link>
                    <description><![CDATA[&lt;p&gt;It&#x27;s been a while since I wrote any kind of lifestyle post, but this one has been sitting in my brain for some time. After joining the organizers team for &lt;a href=&quot;https://codecab.in&quot;&gt;CODECABIN 2026&lt;/a&gt; - which is happening in gorgeous Gleneagle, Colorado in March - and then reading &lt;a href=&quot;https://24days.in/umbraco-cms/2025/passion-isnt-enough/&quot;&gt;Owain&#x27;s blog post for 24 Days in Umbraco&lt;/a&gt;, I&#x27;ve been thinking a lot more about something that I know I have suffered strongly in the past year: burnout.&lt;/p&gt;&#xA;&lt;p&gt;If you haven&#x27;t read Owain&#x27;s post yet, it is also a look back on this last year and a reflection on perfection, dream jobs, and change. It&#x27;s both thoughtful, and thought-provoking, and is the strong impetus behind my own reflection on this phrase that I have been saying in my home the entirety of this last year:&lt;/p&gt;&#xA;&lt;p&gt;Burnout is an injury. It takes time to heal, and it may never heal.&lt;/p&gt;&#xA;&lt;h2 id=&quot;looking-back-at-2025&quot;&gt;Looking Back at 2025&lt;/h2&gt;&#xA;&lt;p&gt;This year was a big year for me - after all, it&#x27;s the 10 year anniversary of &lt;a href=&quot;https://skrift.io&quot;&gt;Skrift&lt;/a&gt;. A year of hype, a year of promotion, a year of improvement! Haha, just kidding. It was in fact Skrift&#x27;s 10 year anniversary, but if we&#x27;re being perfectly honest when the anniversary popped up on my radar in March (we started in April), I wasn&#x27;t ready.&lt;/p&gt;&#xA;&lt;p&gt;Kyle, my husband, was recovering from a long bout of pneumonia and a muscle tear that had landed him in the emergency room. He got diagnosed with that the same day our 17 year old kitty passed away (the first of two pets that we had to say goodbye to this year). Work was incredibly busy and I had a lot on my shoulders at the time - new builds, significant site upgrades/rebuilds, and some big new features for existing clients.&lt;/p&gt;&#xA;&lt;p&gt;When I sat down to think about Skrift, a labor of love for over a decade, I wasn&#x27;t even sure what to say. I couldn&#x27;t focus on it. I had no interest in it. If we&#x27;re being honest? I couldn&#x27;t have cared less about the anniversary.&lt;/p&gt;&#xA;&lt;p&gt;Move forward to planning my travel for Codegarden less than a month before the event, struggling through planning a (rather excellent, if I&#x27;m patting myself on the back) keynote presentation for the US Festival, writing blog posts and articles both for ProWorks, Skrift, and 24 Days...&lt;/p&gt;&#xA;&lt;p&gt;They say it&#x27;s not a sprint, it&#x27;s a marathon, and boy howdy did this year feel like a marathon.&lt;/p&gt;&#xA;&lt;p&gt;I love my job. I love Skrift. I love Umbraco. But I&#x27;m tired.&lt;/p&gt;&#xA;&lt;h2 id=&quot;the-correlation-between-passion-and-burnout&quot;&gt;The Correlation Between Passion and Burnout&lt;/h2&gt;&#xA;&lt;p&gt;And that&#x27;s the crux of my philosophizing in this post. The truth, for me, is you can&#x27;t burn out on something if you weren&#x27;t passionate about it. You cannot suffer burnout if you didn&#x27;t first fall in love.&lt;/p&gt;&#xA;&lt;p&gt;Pacing, thoughtfulness, giving yourself time and space to reflect and ponder doesn&#x27;t happen when you&#x27;re in the middle of a passionate affair with your career, your code, your writing, your art. Being all in, without limits and without boundaries, is how we make amazing progress and also how we break a leg. Or in this case, perhaps break our own hearts a little when we open our laptops, stare at the screen for one, five, fifteen, fourty-five minutes, and then close it again. Because there&#x27;s nothing there to give when the day is over.&lt;/p&gt;&#xA;&lt;h2 id=&quot;its-time-to-balance-your-passion-with-routine&quot;&gt;It&#x27;s Time to Balance Your Passion with Routine&lt;/h2&gt;&#xA;&lt;p&gt;I have been in my current relationship for over fifteen years now, and the last five of them we&#x27;ve been married. If you asked me who my best friend is, I would immediately tell you that it&#x27;s Kyle. And, while this is obviously very personal for an article about burnout in tech, I&#x27;m going to say that if I had tried to make the last fifteen years a wildly passionate fling, I would have absolutely burned out on my relationship as much as I would have any job.&lt;/p&gt;&#xA;&lt;p&gt;But, especially when we have that shiny MVP badge next to our name, there&#x27;s personal pressure that we put on ourselves to constantly maintain that passionate love affair with our work and our code - and it&#x27;s as unrealistic as it is to maintain that level of energy in any interpersonal relationship.&lt;/p&gt;&#xA;&lt;p&gt;What has made my last 15 years successful? Routine - full of easy, comfortable daily moments in each other&#x27;s company, where we have our schedules laid out, our chores separated (I hate the dishes, Kyle hates scooping the litter boxes), and a healthy dose of laughter. And I think this is exactly what makes us successful at careers.&lt;/p&gt;&#xA;&lt;p&gt;I&#x27;m not saying don&#x27;t go above and beyond - obviously there are special moments - or not to push yourself when an opportunity presents itself that sparks your interest. I&#x27;m saying it&#x27;s important to balance your passion with time to relax and let go. Embrace the moments where you aren&#x27;t passionate, where you don&#x27;t want to open the laptop, where the routine is good enough. Let passion be the sprint, not a marathon.&lt;/p&gt;&#xA;&lt;h2 id=&quot;step-away-and-refresh&quot;&gt;Step Away and Refresh&lt;/h2&gt;&#xA;&lt;p&gt;I am proud to say that ProWorks is a strong proponent of work life balance. I can count on one hand the amount of overtime I&#x27;ve worked after starting here three years ago. I also only work four days a week, something that they have supported me in, because I found that when the work week was over, I had no energy for the creative endeavors I love. Having my Mondays free means I actually draw, and I have grown so much as an artist.&lt;/p&gt;&#xA;&lt;p&gt;It&#x27;s important to give ourselves those moments, whether it&#x27;s a randomly taken day off, a shorter work week, or stepping away to spend time with like-minded people in a quiet scenario. I remember being in Pete Duncanson&#x27;s garden in England, talking with a few Umbracians in a personal setting, and feeling so refreshed and genuine.&lt;/p&gt;&#xA;&lt;p&gt;If you get an opportunity - yep, there is a plug, but I honestly mean it - I recommend attending CODECABIN. I went to my first (and only) CODECABIN in 2022 after it had taken a break for COVID. While some people hacked - and yay that passion sprint - I spent most of my time in quiet conversations with people like me, about Umbraco, about life, about retirement (that was one of our big talking points that year), and yeah a little bit about code. It was refreshing, and I wouldn&#x27;t say I came away passionate, I came away thoughtful and feeling like I had bonded with people who could accept me for who I am at this moment. If you get the chance, &lt;a href=&quot;https://codecab.in/#apply2&quot;&gt;you should absolutely go&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Burnout is an injury brought on most often by an overabundance of passion. When you feel it, step back and allow yourself to heal. Remember that you don&#x27;t have to push yourself every day. Routine is absolutely okay - in fact, it&#x27;s healthy. It&#x27;s a sprint, not a marathon.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 11 Dec 2025 12:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/bellissima-backoffice-custom-entity-signs/</guid>
                    <title>Bellissima Backoffice: Custom Entity Signs</title>
                    <link>https://joe.gl/ombek/blog/bellissima-backoffice-custom-entity-signs/</link>
                    <description><![CDATA[&lt;p&gt;You might recognise &quot;entity signs&quot; from the &quot;pending changes&quot; sign in older versions of Umbraco. But now, in Umbraco 17, we can add our own!&lt;/p&gt;&#xA;&lt;h2&gt;Scenario&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You might have seen code like this, that blocks certain document types from being deleted.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;/// A common use case: prevent certain document types being deleted&#xA;public class LockedDocumentContentMovingToRecycleBinNotificationHandler : INotificationHandler&amp;lt;ContentMovingToRecycleBinNotification&amp;gt;&#xA;{&#xA;    public static string[] LOCKED_ALIASES = [&quot;home&quot;, &quot;error&quot;];&#xA;    public static Guid[] LOCKED_IDS = [&#xA;      Guid.Parse(&quot;a95360e8-ff04-40b1-8f46-7aa4b5983096&quot;),&#xA;      Guid.Parse(&quot;9db112c5-c2ea-441d-8bd4-6daf522aa2b6&quot;)&#xA;    ];&#xA;    public void Handle(ContentMovingToRecycleBinNotification notification)&#xA;    {&#xA;        foreach (var item in notification.MoveInfoCollection)&#xA;        {&#xA;            if (Array.Exists(LOCKED_ALIASES, alias =&amp;gt; alias.Equals(item.Entity.ContentType.Alias, StringComparison.OrdinalIgnoreCase)))&#xA;            {&#xA;                notification.CancelOperation(new EventMessage(&#xA;                  $&quot;{item.Entity.Name} cannot be trashed&quot;,&#xA;                  $&quot;The content item &#x27;{item.Entity.Name}&#x27; is of type &#x27;{item.Entity.ContentType.Name}&#x27; which cannot be trashed.&quot;,&#xA;                  EventMessageType.Error));&#xA;            }&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;But wouldn&#x27;t it be nice to show this in the backoffice before trying to delete an item?&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Flagging content items&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The custom signs can be configured to show for items that have a certain &quot;flag&quot;. This is also a new feature of Umbraco 17. (The only use I&#x27;m aware of for flags at the moment is for custom entity signs, but perhaps this will change in the future!)&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;using Umbraco.Cms.Core;&#xA;using Umbraco.Cms.Api.Management.Services.Flags;&#xA;using Umbraco.Cms.Api.Management.ViewModels;&#xA;using Umbraco.Cms.Api.Management.ViewModels.Document.Collection;&#xA;using Umbraco.Cms.Api.Management.ViewModels.Document.Item;&#xA;using Umbraco.Cms.Api.Management.ViewModels.Tree;&#xA;using My.UmbracoBackofficeExtensions.Notifications;&#xA;&#xA;namespace My.UmbracoBackofficeExtensions&#xA;{&#xA;    // Created a C# class that implements `IFlagProvider`&#xA;    public class LockedDocumentFlagProvider : IFlagProvider&#xA;    {&#xA;        // We&#x27;ll use this alias in the custom sign configuration&#xA;        private const string Alias = Constants.Conventions.Flags.Prefix &#x2B; &quot;My.Locked&quot;;&#xA;&#xA;        // Indicate that this flag provider only provides flags for documents.&#xA;        public bool CanProvideFlags&amp;lt;TItem&amp;gt;()&#xA;            where TItem : IHasFlags =&amp;gt;&#xA;            typeof(TItem) == typeof(DocumentTreeItemResponseModel) ||&#xA;            typeof(TItem) == typeof(DocumentCollectionResponseModel) ||&#xA;            typeof(TItem) == typeof(DocumentItemResponseModel);&#xA;&#xA;        // Implemented the `PopulateFlags` method which is just looping through each item and checking it in the `ShouldAddFlag` method.&#xA;        public Task PopulateFlagsAsync&amp;lt;TItem&amp;gt;(IEnumerable&amp;lt;TItem&amp;gt; itemViewModels)&#xA;            where TItem : IHasFlags&#xA;        {&#xA;            foreach (TItem item in itemViewModels)&#xA;            {&#xA;                if (ShouldAddFlag(item))&#xA;                {&#xA;                    item.AddFlag(Alias);&#xA;                }&#xA;            }&#xA;&#xA;            return Task.CompletedTask;&#xA;        }&#xA;&#xA;        // We just get the ID of the document type and check it against our list of IDs we don&#x27;t allow being deleted&#xA;        private bool ShouldAddFlag&amp;lt;TItem&amp;gt;(TItem item)&#xA;        {&#xA;            Guid id;&#xA;            switch (item)&#xA;            {&#xA;                case DocumentTreeItemResponseModel dti:&#xA;                    id = dti.DocumentType.Id;&#xA;                    break;&#xA;                case DocumentCollectionResponseModel dc:&#xA;                    id = dc.DocumentType.Id;&#xA;                    break;&#xA;                case DocumentItemResponseModel di:&#xA;                    id = di.DocumentType.Id;&#xA;                    break;&#xA;                default:&#xA;                    return false;&#xA;            }&#xA;&#xA;            return LockedDocumentContentMovingToRecycleBinNotificationHandler.LOCKED_IDS.Contains(id);&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This provider also needs registering in a composer.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;public class LockedDocumentComposer : IComposer&#xA;{&#xA;    public void Compose(IUmbracoBuilder builder)&#xA;    {&#xA;        builder.SignProviders()&#xA;            .Append&amp;lt;LockedDocumentFlagProvider&amp;gt;();&#xA;&#xA;        // Our existing logic to disallow deleting the document deletion&#xA;        builder.AddNotificationHandler&amp;lt;ContentMovingToRecycleBinNotification, LockedDocumentContentMovingToRecycleBinNotificationHandler&amp;gt;();&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Configuring the custom entity sign&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The entity sign is configured in a backoffice extension. If you&#x27;re already extending the backoffice and have a manifests file already, please read on. Otherwise, I&#x27;ve written about extending the backoffice in my &lt;a href=&quot;https://24days.in/umbraco-cms/2025/template-for-success&quot;&gt;Template for Success&lt;/a&gt; article on 24 Days in Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Once the flag is in place, entity signs only require a manifest to configure them, no JavaScript required:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;import { UMB_DOCUMENT_ENTITY_TYPE } from &#x27;@umbraco-cms/backoffice/document&#x27;;&#xA;&#xA;export const manifests: Array&amp;lt;UmbExtensionManifest&amp;gt; = [&#xA;  // ...&#xA;  // Adding a new manifest of type `enitiySign` and kind `icon`&#xA;  {&#xA;    type: &#x27;entitySign&#x27;,&#xA;    kind: &#x27;icon&#x27;,&#xA;    alias: &#x27;Umb.EntitySign.Document.My.Locked&#x27;,&#xA;    name: &#x27;Is Locked Document Entity Sign&#x27;,&#xA;    // Specifying which enties can show this sign, documents&#xA;    forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],&#xA;    // Specify what entities should be &quot;flagged&quot; with to make the sign show&#xA;    forEntityFlags: [&#x27;Umb.My.Locked&#x27;],&#xA;    // Can only show 2 icons at once, so the weighting matters. `-1000` means this one is really unimportant!&#xA;    weight: -1000,&#xA;    meta: {&#xA;      // Specifying what the sign looks like&#xA;      iconName: &#x27;icon-lock&#x27;,&#xA;      label: &#x27;Locked&#x27;,&#xA;      iconColorAlias: &#x27;red&#x27;,&#xA;    }&#xA;    // You&#x27;ll notice we don&#x27;t link to a TS file! Flagging is purely a C# concern&#xA;  }&#xA;];&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;The result&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;As you can see in the screenshot below, Home and Error are locked and have our new red padlock custom entity sign, while Features and Error have unpublished changes with the default pencil entity sign and Error has both, sorted by priority.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/bd3j4mx4/locked-signs.png&quot; alt=&quot;A screenshot of a content tree in Umbraco with a custom entity sign on the Home and Error nodes. The Features and Error nodes have the default unpublished changes entity sign.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is an example of what entity signs could be used for, but hopefully you can now imagine many more uses! And not just document types either!&lt;/p&gt;]]></description>
                    <pubDate>Thu, 11 Dec 2025 11:28:54 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/addact_technologies_3322b/umbraco-17-lts-features-improvements-what-developers-should-know-234d</guid>
                    <title>Umbraco 17 LTS: Features, Improvements &amp; What Developers Should Know</title>
                    <link>https://dev.to/addact_technologies_3322b/umbraco-17-lts-features-improvements-what-developers-should-know-234d</link>
                    <description><![CDATA[&lt;p&gt;&lt;strong&gt;canonical_url:&lt;/strong&gt; &quot;&lt;a href=&quot;https://www.addact.net/blogs/umbraco-17-lts-features-and-future&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.addact.net/blogs/umbraco-17-lts-features-and-future&lt;/a&gt;&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Every few years, the digital ecosystem takes a big leap forward. Frameworks evolve, performance expectations increase, and businesses look for platforms that can modernize without disrupting their existing operations.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For teams building with Umbraco, this evolution has always felt steady and intentional &#x2014; each release improving the editing experience, strengthening stability, and simplifying the development workflow.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With the launch of Umbraco 17, the newest Long-Term Support (LTS) edition, the platform reaches another important milestone. It&#x27;s not a radical reinvention but a strategic continuation of everything Umbraco does well: usability, flexibility, and long-term reliability.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Why Umbraco 17 Matters&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco 17 is the next LTS after version 13 &#x2014; and the upgrade path is intentionally smooth.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If you&#x2019;re on Umbraco 13, you can directly upgrade to 17.&lt;/li&gt;&#xA;&lt;li&gt;All intermediate migrations are applied automatically.&lt;/li&gt;&#xA;&lt;li&gt;No multi-step upgrade headaches.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;This is especially valuable for enterprise teams that prioritize stability and predictable lifecycles.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Built on .NET 10 LTS, Umbraco 17 aligns with Microsoft&#x2019;s support timeline and will receive security &#x2B; patch updates until late 2028. That gives organizations a strong, future-ready foundation for planning and compliance.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Major Advancements in Umbraco 17&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  1. Load-Balanced Backoffice&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Umbraco 17 introduces true backoffice load balancing, allowing editors across regions to work simultaneously without:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;performance drops&#xA;&lt;/li&gt;&#xA;&lt;li&gt;content conflicts&#xA;&lt;/li&gt;&#xA;&lt;li&gt;task failures &#xA;It especially improves long-running operations and real-time features like SignalR.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  2. Consistent UTC Date &amp;amp; Time Zone Handling&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Older Umbraco versions relied on server-local time, causing issues for:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;multi-region deployments&#xA;&lt;/li&gt;&#xA;&lt;li&gt;daylight-saving differences&#xA;&lt;/li&gt;&#xA;&lt;li&gt;workflows or scheduled publishing&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Umbraco 17 fixes this with:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;All system dates stored in UTC&lt;/li&gt;&#xA;&lt;li&gt;Automatic migration for historical data&lt;/li&gt;&#xA;&lt;li&gt;A new Date-Time with Time Zone property editor&#xA;Scheduling, content workflows, and integrations become far more reliable.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  3. Improved Backoffice UI &amp;amp; Editor Experience&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The editorial experience gets a noticeable polish:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Clearer and more consistent entity icons&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Streamlined navigation&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Accessibility improvements&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Subtle but meaningful speed boosts&#xA;It&#x27;s not flashy, but it makes editing faster and more intuitive.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  4. Stronger Extension Model&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Since the switch to the Bellissima backoffice (v14), Umbraco has been moving toward a modern extensible architecture.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In version 17:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Extensions are now first-class&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Upgrade predictability improves&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Reusable components become more stable&#xA;This is a big win for teams who rely on custom editors, dashboards, or integrations.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Umbraco 17 on Cloud: Built for Scale&lt;br&gt;&#xA;Umbraco Cloud gets several enhancements tailored to enterprise environments:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Better CI/CD tooling&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Hostname validation&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Centralized identity management&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Bandwidth insights&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Reliable scaling for high-traffic workloads&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Plus, Umbraco Engage now aligns with the new backoffice and supports Deploy, making personalization moves across environments seamless.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Developer-Focused Enhancements&lt;br&gt;&#xA;Umbraco 17 brings upgrades that directly improve engineering workflows:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;TipTap becomes the default rich-text editor&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Server-side preview URLs&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Optimized dependency layer for .NET 10&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Faster Models Builder performance&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Together, these reduce debugging friction and improve day-to-day development speed.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Installing Umbraco 17 (Quick Guide)&lt;br&gt;&#xA;You&#x2019;ll need:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;.NET 10 or later&#xA;&lt;/li&gt;&#xA;&lt;li&gt;VS Code or Visual Studio 2022&#xA;&lt;/li&gt;&#xA;&lt;li&gt;SQLite or SQL Server (2016&#x2B;)&#xA;&lt;/li&gt;&#xA;&lt;li&gt;A modern browser&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;]]></description>
                    <pubDate>Thu, 11 Dec 2025 09:13:58 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/umbraco-search/</guid>
                    <title>Umbraco Search is coming to town</title>
                    <link>https://24days.in/umbraco-cms/2025/umbraco-search/</link>
                    <description><![CDATA[You better watch out, you better not cry. You better not pout, I&#x27;m telling you why: Umbraco Search is coming to town!]]></description>
                    <pubDate>Thu, 11 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/slimpim.slimconnect</guid>
                    <title>SlimPim.SlimConnect</title>
                    <link>https://marketplace.umbraco.com/package/slimpim.slimconnect</link>
                    <description><![CDATA[SlimConnect integrates SlimPim (Product Information Management) with Umbraco CMS. Automatically sync your product catalog, categories, variants, images and custom fields from SlimPim to Umbraco content nodes. Includes backoffice dashboard, scheduled sync, and frontend helper services.]]></description>
                    <pubDate>Thu, 11 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/belissima-backoffice-blocks-in-the-rte/</guid>
                    <title>Belissima Backoffice: Blocks in the Rich Text Editor</title>
                    <link>https://joe.gl/ombek/blog/belissima-backoffice-blocks-in-the-rte/</link>
                    <description><![CDATA[&lt;p&gt;We&#x27;ve been able to add blocks (as seen in Block List and Block Grid) to the Rich Text Editor (RTE) since Umbraco 13, but they&#x27;re topical at the moment with v17 being released, the first LTS version since macros have been removed, more of us are looking to &lt;a href=&quot;/ombek/blog/migrating-rte-macros/&quot;&gt;replace macros&lt;/a&gt; for good!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;RTE Blocks are a new way of thinking about content (and a good replacement for macros!)&lt;/p&gt;&#xA;&lt;h2&gt;Creating block types&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You can reuse your existing Block List and Block Grid blocks - they&#x27;re exactly the same thing! But in most real use cases these will be different document types.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You simply need to create &quot;element&quot; type document types with all the properties you want on your new block, the difference comes in the data type you configure them in:&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Adding blocks to the RTE&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Instead of creating a Block List or Block Grid, we need to create (or modify an existing) Rich Text Editor data type.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We&#x27;ll need to add the Block button to the RTE toolbar.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/fwsadqij/rte-toolbar-settings.png&quot; alt=&quot;A screenshot of the RTE toolbar settings with an arrow indicating dragging the Blocks button to toolbar.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The RTE now has settings for configuring Blocks, just like in other Block editors:&#xA;&lt;img src=&quot;/media/i00djapz/rte-blocks-settings.png&quot; alt=&quot;A screenshot of the block configuration section of the RTE settings showing one configured for &amp;quot;Phone Number RTE Block&amp;quot;&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Select your element types with optional settings and configure a label template, just as you would ordinarily as well as a setting for whether the block will render inline (think &lt;code&gt;span&lt;/code&gt;) or as a block (think &lt;code&gt;div&lt;/code&gt;).&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Rendering RTE Blocks&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This is probably where the RTE block differs most from the other block editors (and even that doesn&#x27;t differ much!)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Simply needs a view matching the alias in the &lt;code&gt;Views\Partials\RichText\Components&lt;/code&gt; folder.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s my example phone number view located at &lt;code&gt;TemplateForSuccess.Web\Views\Partials\RichText\Components\PhoneNumberRteBlock.cshtml&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage&amp;lt;Umbraco.Cms.Core.Models.Blocks.RichTextBlockItem&amp;lt;ContentModels.PhoneNumberRteBlock&amp;gt;&amp;gt;&#xA;@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;&#xA;@using System.Text.RegularExpressions&#xA;@{&#xA;    var contactPage = Model.Content.ContactUsPage as ContentModels.Contact;&#xA;&#xA;    if(contactPage is null || string.IsNullOrWhiteSpace(contactPage.PhoneNumber))&#xA;    {&#xA;        return;&#xA;    }&#xA;}&#xA;&#xA;&amp;lt;a href=&quot;tel:@contactPage.PhoneNumber.Replace(&quot; &quot;, &quot;&quot;)&quot;&amp;gt;&#xA;    @Regex.Replace(contactPage.PhoneNumber, @&quot;\&#x2B;44\s*&quot;, &quot;0&quot;)&#xA;&amp;lt;/a&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;In this example I&#x27;m rendering a phone number from the selected contact page (a property on the Block), applying all the logic I want to format the phone number and adding the &lt;code&gt;tel:&lt;/code&gt; link.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The journey for adding this block via the backoffice looks like this:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Click the insert block button&lt;/li&gt;&#xA;&lt;li&gt;Pick my phone number block&lt;/li&gt;&#xA;&lt;li&gt;Complete the content for the block, a content picker for the contact page in my case&lt;/li&gt;&#xA;&lt;li&gt;Save&lt;/li&gt;&#xA;&lt;li&gt;And a placeholder appears inline in the RTE&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/33vn5xeq/rte-block.apng&quot; alt=&quot;A screen capture showing the steps above.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Which, when viewed on the frontend renders my view:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/0jsnhour/rte-blocks-rendered.png&quot; alt=&quot;A screenshot of the RTE contents with the RTE Block replaced by the markup from my view.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Further reading&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;/ombek/blog/migrating-rte-macros/&quot;&gt;Migrating Rich Text Editor Macros to Blocks using uSync Migrations&lt;/a&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 10 Dec 2025 11:32:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/bellissima-backoffice-blocks-in-the-rte/</guid>
                    <title>Bellissima Backoffice: Blocks in the Rich Text Editor</title>
                    <link>https://joe.gl/ombek/blog/bellissima-backoffice-blocks-in-the-rte/</link>
                    <description><![CDATA[&lt;p&gt;We&#x27;ve been able to add blocks (as seen in Block List and Block Grid) to the Rich Text Editor (RTE) since Umbraco 13, but they&#x27;re topical at the moment with v17 being released, the first LTS version since macros have been removed, more of us are looking to &lt;a href=&quot;/ombek/blog/migrating-rte-macros/&quot;&gt;replace macros&lt;/a&gt; for good!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;RTE Blocks are a new way of thinking about content (and a good replacement for macros!)&lt;/p&gt;&#xA;&lt;h2&gt;Creating block types&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You can reuse your existing Block List and Block Grid blocks - they&#x27;re exactly the same thing! But in most real use cases these will be different document types.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You simply need to create &quot;element&quot; type document types with all the properties you want on your new block, the difference comes in the data type you configure them in:&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Adding blocks to the RTE&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Instead of creating a Block List or Block Grid, we need to create (or modify an existing) Rich Text Editor data type.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We&#x27;ll need to add the Block button to the RTE toolbar.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/fwsadqij/rte-toolbar-settings.png&quot; alt=&quot;A screenshot of the RTE toolbar settings with an arrow indicating dragging the Blocks button to toolbar.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The RTE now has settings for configuring Blocks, just like in other Block editors:&#xA;&lt;img src=&quot;/media/i00djapz/rte-blocks-settings.png&quot; alt=&quot;A screenshot of the block configuration section of the RTE settings showing one configured for &amp;quot;Phone Number RTE Block&amp;quot;&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Select your element types with optional settings and configure a label template, just as you would ordinarily as well as a setting for whether the block will render inline (think &lt;code&gt;span&lt;/code&gt;) or as a block (think &lt;code&gt;div&lt;/code&gt;).&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Rendering RTE Blocks&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This is probably where the RTE block differs most from the other block editors (and even that doesn&#x27;t differ much!)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It simply needs a view matching the alias in the &lt;code&gt;Views\Partials\RichText\Components&lt;/code&gt; folder.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s my example phone number view located at &lt;code&gt;TemplateForSuccess.Web\Views\Partials\RichText\Components\PhoneNumberRteBlock.cshtml&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage&amp;lt;Umbraco.Cms.Core.Models.Blocks.RichTextBlockItem&amp;lt;ContentModels.PhoneNumberRteBlock&amp;gt;&amp;gt;&#xA;@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;&#xA;@using System.Text.RegularExpressions&#xA;@{&#xA;    var contactPage = Model.Content.ContactUsPage as ContentModels.Contact;&#xA;&#xA;    if(contactPage is null || string.IsNullOrWhiteSpace(contactPage.PhoneNumber))&#xA;    {&#xA;        return;&#xA;    }&#xA;}&#xA;&#xA;&amp;lt;a href=&quot;tel:@contactPage.PhoneNumber.Replace(&quot; &quot;, &quot;&quot;)&quot;&amp;gt;&#xA;    @Regex.Replace(contactPage.PhoneNumber, @&quot;\&#x2B;44\s*&quot;, &quot;0&quot;)&#xA;&amp;lt;/a&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;In this example I&#x27;m rendering a phone number from the selected contact page (a property on the Block), applying all the logic I want to format the phone number and adding the &lt;code&gt;tel:&lt;/code&gt; link.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The journey for adding this block via the backoffice looks like this:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Click the insert block button&lt;/li&gt;&#xA;&lt;li&gt;Pick my phone number block&lt;/li&gt;&#xA;&lt;li&gt;Complete the content for the block, a content picker for the contact page in my case&lt;/li&gt;&#xA;&lt;li&gt;Save&lt;/li&gt;&#xA;&lt;li&gt;And a placeholder appears inline in the RTE&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/33vn5xeq/rte-block.apng&quot; alt=&quot;A screen capture showing the steps above.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Which, when viewed on the frontend renders my view:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/0jsnhour/rte-blocks-rendered.png&quot; alt=&quot;A screenshot of the RTE contents with the RTE Block replaced by the markup from my view.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Further reading&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;/ombek/blog/migrating-rte-macros/&quot;&gt;Migrating Rich Text Editor Macros to Blocks using uSync Migrations&lt;/a&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 10 Dec 2025 11:32:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/importing-blocklist-items/</guid>
                    <title>Importing Block List Items Programmatically in Umbraco 17</title>
                    <link>https://24days.in/umbraco-cms/2025/importing-blocklist-items/</link>
                    <description><![CDATA[A practical, problem-solving guide on how to import Block List items programmatically in Umbraco 17 instead of manually creating them in the backoffice.]]></description>
                    <pubDate>Wed, 10 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/bellissima-backoffice-custom-block-views/</guid>
                    <title>Bellissima Backoffice: Custom block views</title>
                    <link>https://joe.gl/ombek/blog/bellissima-backoffice-custom-block-views/</link>
                    <description><![CDATA[&lt;p&gt;Custom block views have changed with recent versions, so let&#x27;s take a look at how we might do this.&lt;/p&gt;&#xA;&lt;p&gt;Custom backoffice block views are now configured using an Umbraco backoffice extension.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you&#x27;re already extending the backoffice and have a manifests file already, please read on. Otherwise, I&#x27;ve written about extending the backoffice in my &lt;a href=&quot;https://24days.in/umbraco-cms/2025/template-for-success&quot;&gt;Template for Success&lt;/a&gt; article on 24 Days in Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;The example&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In this post I&#x27;ll be using the example of showing a little tag to indicate a block is hidden. I&#x27;m using the familiar pattern used by Clean Starter Kit of having a&lt;code&gt;hidden&lt;/code&gt; setting on each block. Rather than rendering the text &quot;[HIDDEN]&quot; in each block template I&#x27;d like this to automatically appear for all blocks. Here&#x27;s a mockup I made in Paint of what we&#x27;re aiming for here.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/nligk5hw/blocks-component-mockup.png&quot; alt=&quot;A screenshot of a block list where some elements have a &amp;quot;Hidden&amp;quot; tag at the end of the text.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Registering the extension&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Add a manifest to your manifests file:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;export const manifests: Array&amp;lt;UmbExtensionManifest&amp;gt; = [&#xA;  // ...&#xA;  {&#xA;    // extension type is `blockEditorCustomView`&#xA;    type: &#x27;blockEditorCustomView&#x27;,&#xA;    alias: &#x27;My.HiddenBlockEditorView&#x27;,&#xA;    name: &#x27;Hidden Block Editor View&#x27;,&#xA;    // referencing a TS file for the element we want to use a the block view&#xA;    element: () =&amp;gt; import(&#x27;./hidden-block.element&#x27;),&#xA;    // specifying which block editor we&#x27;re replacing the view for&#xA;    forBlockEditor: &#x27;block-list&#x27;&#xA;  }&#xA;];&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Creating the custom element&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;That referenced &lt;code&gt;hidden-block.element.ts&lt;/code&gt; file looks like this:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;import { html, customElement, LitElement, property, css, when } from &#x27;@umbraco-cms/backoffice/external/lit&#x27;;&#xA;import { UmbElementMixin } from &#x27;@umbraco-cms/backoffice/element-api&#x27;;&#xA;import type { UmbBlockDataType } from &#x27;@umbraco-cms/backoffice/block&#x27;;&#xA;import type { UmbBlockEditorCustomViewElement, UmbBlockEditorCustomViewConfiguration } from &#x27;@umbraco-cms/backoffice/block-custom-view&#x27;;&#xA;&#xA;@customElement(&#x27;hidden-block-custom-view&#x27;)&#xA;// Extending the `UmbElementMixin` and implementing `UmbBlockEditorCustomViewElement`&#xA;export class HiddenBlockCustomView extends UmbElementMixin(LitElement) implements UmbBlockEditorCustomViewElement {&#xA;&#xA;    // UmbRefListBlockElement is not exposed to extend it, so we have to copy a lot of it in to replace it:&#xA;&#xA;    @property({ type: String, reflect: false })&#xA;    label?: string;&#xA;   // ...&#xA;   // Other properties copied from UmbRefListBlockElement&#xA;   // ...&#xA;&#xA;    // This render method is largely copied from UmbRefListBlockElement, with some customisations to show hidden state&#xA;    override render() {&#xA;        const blockValue = { ...this.content, $settings: this.settings, $index: this.index };&#xA;        return html`&#xA;            &amp;lt;uui-ref-node standalone&#xA;        href=${(this.config?.showContentEdit ? this.config?.editContentPath : undefined) ?? &#x27;&#x27;}&#xA;        class=&quot;${this.settings?.hide ? &#x27;hidden&#x27; : &#x27;&#x27;}&quot;&amp;gt;&#xA;            ${when(&#xA;                this.settings?.hide,&#xA;                () =&amp;gt;&#xA;                    html`&amp;lt;umb-icon slot=&quot;icon&quot; name=&quot;icon-checkbox-dotted&quot;&amp;gt;&amp;lt;/umb-icon&amp;gt;`,&#xA;                () =&amp;gt;&#xA;                    html`&amp;lt;umb-icon slot=&quot;icon&quot; .name=${this.icon}&amp;gt;&amp;lt;/umb-icon&amp;gt;`&#xA;            )}&#xA;                &amp;lt;umb-ufm-render slot=&quot;name&quot; inline .markdown=${this.label} .value=${blockValue}&amp;gt;&amp;lt;/umb-ufm-render&amp;gt;&#xA;                ${when(&#xA;                    this.unpublished,&#xA;                    () =&amp;gt;&#xA;                        html`&amp;lt;uui-tag slot=&quot;name&quot; look=&quot;secondary&quot; title=${this.localize.term(&#x27;blockEditor_notExposedDescription&#x27;)}&#xA;                                    &amp;gt;&amp;lt;umb-localize key=&quot;blockEditor_notExposedLabel&quot;&amp;gt;&amp;lt;/umb-localize&#xA;                                &amp;gt;&amp;lt;/uui-tag&amp;gt;`,&#xA;                )}&#xA;                ${when(&#xA;                    this.settings?.hide,&#xA;                    () =&amp;gt;&#xA;                        html`&amp;lt;uui-tag slot=&quot;name&quot; look=&quot;secondary&quot; title=&quot;Hidden&quot;&#xA;                                &amp;gt;Hidden&amp;lt;/umb-localize&#xA;                            &amp;gt;&amp;lt;/uui-tag&amp;gt;`&#xA;                )}&#xA;            &amp;lt;/uui-ref-node&amp;gt;&#xA;        `;&#xA;    }&#xA;&#xA;    static override styles = [&#xA;        css`&#xA;            /* Copied styles from UmbRefListBlockElement and added custom styles /*&#xA;        `,&#xA;    ];&#xA;}&#xA;export default HiddenBlockCustomView;&#xA;&#xA;declare global {&#xA;    interface HTMLElementTagNameMap {&#xA;        &#x27;hidden-block-custom-view&#x27;: HiddenBlockCustomView;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://github.com/slidejoe/template-for-success/blob/main/samples/My.UmbracoBackofficeExtensions/Client/src/hidden-block.element.ts&quot;&gt;The full code sample is available on GitHub.&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;The new view!&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With that in place, my block lists now look like this!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/mqfervaj/block-custom-view-no-template.png&quot; alt=&quot;A screenshot of a block list where some elements have a &amp;quot;Hidden&amp;quot; tag at the end of the text, are greyed out with a dashed-square icon&quot; /&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 10 Dec 2025 05:20:34 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/belissima-backoffice-custom-block-views/</guid>
                    <title>Belissima Backoffice: Custom block views</title>
                    <link>https://joe.gl/ombek/blog/belissima-backoffice-custom-block-views/</link>
                    <description><![CDATA[&lt;p&gt;Custom block views have changed with recent versions, so let&#x27;s take a look at how we might do this.&lt;/p&gt;&#xA;&lt;p&gt;Custom backoffice block views are now configured using an Umbraco backoffice extension.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you&#x27;re already extending the backoffice and have a manifests file already, please read on. Otherwise, I&#x27;ve written about extending the backoffice in my &lt;a href=&quot;https://24days.in/umbraco-cms/2025/template-for-success&quot;&gt;Template for Success&lt;/a&gt; article on 24 Days in Umbraco.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;The example&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In this post I&#x27;ll be using the example of showing a little tag to indicate a block is hidden. I&#x27;m using the familiar pattern used by Clean Starter Kit of having a&lt;code&gt;hidden&lt;/code&gt; setting on each block. Rather than rendering the text &quot;[HIDDEN]&quot; in each block template I&#x27;d like this to automatically appear for all blocks. Here&#x27;s a mockup I made in Paint of what we&#x27;re aiming for here.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/nligk5hw/blocks-component-mockup.png&quot; alt=&quot;A screenshot of a block list where some elements have a &amp;quot;Hidden&amp;quot; tag at the end of the text.&quot; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Registering the extension&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Add a manifest to your manifests file:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;export const manifests: Array&amp;lt;UmbExtensionManifest&amp;gt; = [&#xA;  // ...&#xA;  {&#xA;    // extension type is `blockEditorCustomView`&#xA;    type: &#x27;blockEditorCustomView&#x27;,&#xA;    alias: &#x27;My.HiddenBlockEditorView&#x27;,&#xA;    name: &#x27;Hidden Block Editor View&#x27;,&#xA;    // referencing a TS file for the element we want to use a the block view&#xA;    element: () =&amp;gt; import(&#x27;./hidden-block.element&#x27;),&#xA;    // specifying which block editor we&#x27;re replacing the view for&#xA;    forBlockEditor: &#x27;block-list&#x27;&#xA;  }&#xA;];&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Creating the custom element&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;That referenced &lt;code&gt;hidden-block.element.ts&lt;/code&gt; file looks like this:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;import { html, customElement, LitElement, property, css, when } from &#x27;@umbraco-cms/backoffice/external/lit&#x27;;&#xA;import { UmbElementMixin } from &#x27;@umbraco-cms/backoffice/element-api&#x27;;&#xA;import type { UmbBlockDataType } from &#x27;@umbraco-cms/backoffice/block&#x27;;&#xA;import type { UmbBlockEditorCustomViewElement, UmbBlockEditorCustomViewConfiguration } from &#x27;@umbraco-cms/backoffice/block-custom-view&#x27;;&#xA;&#xA;@customElement(&#x27;hidden-block-custom-view&#x27;)&#xA;// Extending the `UmbElementMixin` and implementing `UmbBlockEditorCustomViewElement`&#xA;export class HiddenBlockCustomView extends UmbElementMixin(LitElement) implements UmbBlockEditorCustomViewElement {&#xA;&#xA;    // UmbRefListBlockElement is not exposed to extend it, so we have to copy a lot of it in to replace it:&#xA;&#xA;    @property({ type: String, reflect: false })&#xA;    label?: string;&#xA;   // ...&#xA;   // Other properties copied from UmbRefListBlockElement&#xA;   // ...&#xA;&#xA;    // This render method is largely copied from UmbRefListBlockElement, with some customisations to show hidden state&#xA;    override render() {&#xA;        const blockValue = { ...this.content, $settings: this.settings, $index: this.index };&#xA;        return html`&#xA;            &amp;lt;uui-ref-node standalone&#xA;        href=${(this.config?.showContentEdit ? this.config?.editContentPath : undefined) ?? &#x27;&#x27;}&#xA;        class=&quot;${this.settings?.hide ? &#x27;hidden&#x27; : &#x27;&#x27;}&quot;&amp;gt;&#xA;            ${when(&#xA;                this.settings?.hide,&#xA;                () =&amp;gt;&#xA;                    html`&amp;lt;umb-icon slot=&quot;icon&quot; name=&quot;icon-checkbox-dotted&quot;&amp;gt;&amp;lt;/umb-icon&amp;gt;`,&#xA;                () =&amp;gt;&#xA;                    html`&amp;lt;umb-icon slot=&quot;icon&quot; .name=${this.icon}&amp;gt;&amp;lt;/umb-icon&amp;gt;`&#xA;            )}&#xA;                &amp;lt;umb-ufm-render slot=&quot;name&quot; inline .markdown=${this.label} .value=${blockValue}&amp;gt;&amp;lt;/umb-ufm-render&amp;gt;&#xA;                ${when(&#xA;                    this.unpublished,&#xA;                    () =&amp;gt;&#xA;                        html`&amp;lt;uui-tag slot=&quot;name&quot; look=&quot;secondary&quot; title=${this.localize.term(&#x27;blockEditor_notExposedDescription&#x27;)}&#xA;                                    &amp;gt;&amp;lt;umb-localize key=&quot;blockEditor_notExposedLabel&quot;&amp;gt;&amp;lt;/umb-localize&#xA;                                &amp;gt;&amp;lt;/uui-tag&amp;gt;`,&#xA;                )}&#xA;                ${when(&#xA;                    this.settings?.hide,&#xA;                    () =&amp;gt;&#xA;                        html`&amp;lt;uui-tag slot=&quot;name&quot; look=&quot;secondary&quot; title=&quot;Hidden&quot;&#xA;                                &amp;gt;Hidden&amp;lt;/umb-localize&#xA;                            &amp;gt;&amp;lt;/uui-tag&amp;gt;`&#xA;                )}&#xA;            &amp;lt;/uui-ref-node&amp;gt;&#xA;        `;&#xA;    }&#xA;&#xA;    static override styles = [&#xA;        css`&#xA;            /* Copied styles from UmbRefListBlockElement and added custom styles /*&#xA;        `,&#xA;    ];&#xA;}&#xA;export default HiddenBlockCustomView;&#xA;&#xA;declare global {&#xA;    interface HTMLElementTagNameMap {&#xA;        &#x27;hidden-block-custom-view&#x27;: HiddenBlockCustomView;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://github.com/slidejoe/template-for-success/blob/main/samples/My.UmbracoBackofficeExtensions/Client/src/hidden-block.element.ts&quot;&gt;The full code sample is available on GitHub.&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;The new view!&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;With that in place, my block lists now look like this!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/mqfervaj/block-custom-view-no-template.png&quot; alt=&quot;A screenshot of a block list where some elements have a &amp;quot;Hidden&amp;quot; tag at the end of the text, are greyed out with a dashed-square icon&quot; /&gt;&lt;/p&gt;]]></description>
                    <pubDate>Wed, 10 Dec 2025 05:20:34 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/paul-seal-3064892_umbraco-community-tool-ugcPost-7404203334707859456--1cx/</guid>
                    <title>CLI tool for Package Script Writer now available</title>
                    <link>https://www.linkedin.com/posts/paul-seal-3064892_umbraco-community-tool-ugcPost-7404203334707859456--1cx/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 09 Dec 2025 17:31:37 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>linkedin</category>
                </item>
                <item>
                    <guid>https://obsidian.md/plugins?search=Umbraco</guid>
                    <title>umbPublisher 1.3.0 is now out</title>
                    <link>https://obsidian.md/plugins?search=Umbraco</link>
                    <description><![CDATA[umbPublisher 1.3.0 is now out. This release now lets you push images to Umbraco as part of your content. Got images in your markdown file, no problem, the media is saved in an &quot;obsidian&quot; folder and the links are updated in your markdown file. https://obsidian.md/plugins?search=Umbraco#umbraco #obsidian]]></description>
                    <pubDate>Tue, 09 Dec 2025 16:40:59 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://sessionize.com/umbraco-spark-2026/</guid>
                    <title>Submit your talk proposal for Umbraco Spark by January 5th</title>
                    <link>https://sessionize.com/umbraco-spark-2026/</link>
                    <description><![CDATA[&#x1F3A4; Why speak at #UmbracoSpark?2024 speaker @erica shares:&#x201C;Umbraco Spark was incredibly welcoming and inspiring&#x2026; seeing both sides connect with the takeaways was so validating. The community&#x2019;s curiosity and warmth made it an amazing place to learn, connect, and contribute.&#x201D;&#x1F381; Free ticket &#x2B; accommodation&#x1F449; Submit your proposal: https://sessionize.com/umbraco-spark-2026/&#x1F55B; CFP closes 5 January at midnight#Umbraco #Innovation #CallForSpeakers]]></description>
                    <pubDate>Tue, 09 Dec 2025 12:05:19 Z</pubDate>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/backoffice-communication/</guid>
                    <title>Communication in the Backoffice</title>
                    <link>https://24days.in/umbraco-cms/2025/backoffice-communication/</link>
                    <description><![CDATA[A great customization integrates seamlessly &#x2014; and the Context API makes that possible. In this article, I&#x2019;ll show you how it works and how you can make the most of it.]]></description>
                    <pubDate>Tue, 09 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/security-advisory-december-9-2025-security-patch-is-now-available/</guid>
                    <title>Security Advisory, December 9, 2025: Security Patch is now available</title>
                    <link>https://umbraco.com/blog/security-advisory-december-9-2025-security-patch-is-now-available/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 09 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/Ambertvu/Umbraco.Community.IconPicker</guid>
                    <title>Umbraco.Community.IconPicker</title>
                    <link>https://github.com/Ambertvu/Umbraco.Community.IconPicker</link>
                    <description><![CDATA[Happy to announce my first package! Originally build it for an own project. But now its out for the public!&#xA;&#xA;Currently only for V13, but started work on a v17 version already!&#xA;&#xA;Description should supply all that is needed: https://github.com/Ambertvu/Umbraco.Community.IconPicker]]></description>
                    <pubDate>Mon, 08 Dec 2025 13:16:43 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/umbraco-compose-introduction/</guid>
                    <title>Umbraco Compose - Under the Tree</title>
                    <link>https://24days.in/umbraco-cms/2025/umbraco-compose-introduction/</link>
                    <description><![CDATA[A sneak peek at the orchestration platform, launching Q1 2026 - still too early for unwrapping, but...]]></description>
                    <pubDate>Mon, 08 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.addact.net/blogs/umbraco-17-lts-features-and-future</guid>
                    <title>Umbraco 17 LTS: Features, Upgrades &amp; Benefits for Modern CMS</title>
                    <link>https://www.addact.net/blogs/umbraco-17-lts-features-and-future</link>
                    <description><![CDATA[Umbraco 17 LTS: Features, Upgrades &amp; Benefits for Modern CMS]]></description>
                    <pubDate>Mon, 08 Dec 2025 06:41:40 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/why-ai-needs-a-platform-not-just-potential/</guid>
                    <title>Why AI Needs a Platform, Not Just Potential</title>
                    <link>https://umbraco.com/blog/why-ai-needs-a-platform-not-just-potential/</link>
                    <description><![CDATA[Mats Persson, Umbraco CEO, explains why AI needs a CMS platform]]></description>
                    <pubDate>Mon, 08 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.pagespeedoptimizer</guid>
                    <title>Umbraco.Community.PagespeedOptimizer</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.pagespeedoptimizer</link>
                    <description><![CDATA[This Umbraco package improves your Google page speed score. This package automatically optimizes your site&#x27;s front-end performance with zero coding required.]]></description>
                    <pubDate>Mon, 08 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://cultiv.social/@sebastiaan/115678715927663519</guid>
                    <title>Cultiv.Hangfire 5.2.0 released</title>
                    <link>https://cultiv.social/@sebastiaan/115678715927663519</link>
                    <description><![CDATA[I&#x27;ve had a productive weekend on my hobby projects &#x1F64C;`Cultiv.Hangfire` is now at 5.2.0 after merging 2 community PRs and implementing an idea Bjarne had years ago to move the Hangfire dashboard to it&#x27;s own section - that is now an option! Release notes: https://github.com/nul800sebastiaan/Cultiv.Hangfire/releases/tag/v5.1.0 and https://github.com/nul800sebastiaan/Cultiv.Hangfire/releases/tag/v5.2.0Also: https://umbracalendar.com now has a stylesheet for it&#x27;s RSS feed and the feed includes all events, not just the ones from meetup.com &#x1F389; Feed: https://umbracalendar.com/meetup#Umbraco]]></description>
                    <pubDate>Sun, 07 Dec 2025 19:57:10 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/demystifying/</guid>
                    <title>On Demystifying Our Work</title>
                    <link>https://24days.in/umbraco-cms/2025/demystifying/</link>
                    <description><![CDATA[As web workers, we often mystify clients with jargon and complexity. Here&#x27;s how to bridge the gap through context, translation, involvement, and trust in your web work.]]></description>
                    <pubDate>Sun, 07 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/extendeverything.tetrisdashboard</guid>
                    <title>ExtendEverything.TetrisDashboard</title>
                    <link>https://marketplace.umbraco.com/package/extendeverything.tetrisdashboard</link>
                    <description><![CDATA[Adds a Tetris dashboard to the Umbraco 17 backoffice.]]></description>
                    <pubDate>Sun, 07 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/headless-umbraco/</guid>
                    <title>Headless Umbraco: Two Paths to JSON Glory</title>
                    <link>https://24days.in/umbraco-cms/2025/headless-umbraco/</link>
                    <description><![CDATA[Sometimes you need Umbraco to just handle content while your frontend does its own thing. This article compares two approaches: Umbraco&#x27;s built-in Content Delivery API versus building your own custom solution. We&#x27;ll look at real code, and help you figure out which one actually fits your project.]]></description>
                    <pubDate>Sat, 06 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/gotik.commerce</guid>
                    <title>Gotik.Commerce</title>
                    <link>https://marketplace.umbraco.com/package/gotik.commerce</link>
                    <description><![CDATA[Complete e-commerce solution for Umbraco CMS including backend services, API controllers,&#xA;      product catalog with dynamic filtering, shopping cart, order management, and Odoo ERP integration.&#xA;      Built with clean architecture principles and enterprise-grade features.]]></description>
                    <pubDate>Sat, 06 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.lexicon</guid>
                    <title>Umbraco.Community.Lexicon</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.lexicon</link>
                    <description><![CDATA[Lexicon restores dictionary-based backoffice localization for Umbraco 14&#x2B;. Use dictionary items to translate document type labels, descriptions, tab names, and group names in the backoffice.]]></description>
                    <pubDate>Sat, 06 Dec 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-recognized-as-a-g2-leader-across-cms-and-headless-in-the-winter-2026-reports/</guid>
                    <title>Umbraco Recognized as a G2 Leader Across CMS and Headless in the Winter 2026 Reports</title>
                    <link>https://umbraco.com/blog/umbraco-recognized-as-a-g2-leader-across-cms-and-headless-in-the-winter-2026-reports/</link>
                    <description><![CDATA[Discover why Umbraco is recognized as a G2 Leader in CMS, Headless, DXP, and WebOps in the Winter 2026 Reports highlighting top usability, fast implementation, and strong global adoption backed by 936&#x2B; user reviews.]]></description>
                    <pubDate>Sat, 06 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nathanielnunes.com/blog/find-ancestors-of-item-using-the-umbraco-content-delivery-api</guid>
                    <title>Find Ancestors of Item Using the Umbraco Content Delivery API</title>
                    <link>https://www.nathanielnunes.com/blog/find-ancestors-of-item-using-the-umbraco-content-delivery-api</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 05 Dec 2025 13:50:40 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/twas-the-night/</guid>
                    <title>&#x27;Twas the Night Before v17 Release</title>
                    <link>https://24days.in/umbraco-cms/2025/twas-the-night/</link>
                    <description><![CDATA[A playful, holiday-style ode to the excitement surrounding the Umbraco v17 release, this poem follows a developer&#x2019;s sleepless anticipation as they await new features, celebrate major technical improvements&#x2014;from a redesigned back office to modernized front-end tooling&#x2014;and applaud the entire Umbraco community. Blending humor, gratitude, and tech enthusiasm, it captures the collective joy of Release Day.]]></description>
                    <pubDate>Fri, 05 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=tDoEAf37Rn8</guid>
                    <title>Codegarden 2026 Tickets Are LIVE!</title>
                    <link>https://www.youtube.com/watch?v=tDoEAf37Rn8</link>
                    <description><![CDATA[Join us in Copenhagen (or online) on June 10&#x2013;11 for the biggest Umbraco conference in the world - two full days of learning, genuine conversations, and the kind of inspiration that brings business leaders, developers, and digital creators together.&#xA;&#xA;Read more and secure your spot at: https://codegarden.umbraco.com/]]></description>
                    <pubDate>Thu, 04 Dec 2025 09:35:42 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/umbazing-technical-articles/</guid>
                    <title>Tips to Make Your Technical Articles Umbazing</title>
                    <link>https://24days.in/umbraco-cms/2025/umbazing-technical-articles/</link>
                    <description><![CDATA[Technical writing is a very different beast from fiction, poetry, or journaling. It often aligns more with an essay, but if it&#x27;s too stiff and dry then what makes it different than documentation? This is already getting complicated, but don&#x27;t worry! I&#x27;m here with a bunch of tips to level up your technical writing, or even just get you started if this is a new journey for you.]]></description>
                    <pubDate>Thu, 04 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.proworks.com/blog/archive/tinymceumbraco-v17-released-and-ready-for-umbraco-17/</guid>
                    <title>TinyMCE.Umbraco v17 Released and Ready for Umbraco 17</title>
                    <link>https://www.proworks.com/blog/archive/tinymceumbraco-v17-released-and-ready-for-umbraco-17/</link>
                    <description><![CDATA[&lt;p&gt;The ProWorks team is excited to announce the release of TinyMCE.Umbraco v17, our latest update to the &lt;a rel=&quot;noopener&quot; href=&quot;https://github.com/ProWorksCorporation/TinyMCE-Umbraco&quot; target=&quot;_blank&quot;&gt;TinyMCE.Umbraco package&lt;/a&gt; for Umbraco CMS. If you&#x27;re using or upgrading to Umbraco 17 and want to continue using TinyMCE, this update ensures full compatibility with the new Web Components-based back-office and adds in some other great features as well:&lt;/p&gt;&#xA;&lt;p&gt;Additional features include:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Streamlined configuration for RTE Data Types in Umbraco&lt;/li&gt;&#xA;&lt;li&gt;Enhanced settings that support direct JSON-based configuration via .NET (&lt;code&gt;appsettings.json&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Support for updating TinyMCE to versions 7 or 8&lt;/li&gt;&#xA;&lt;li&gt;Simple configuration-based TinyMCE Premium setup&lt;/li&gt;&#xA;&lt;li&gt;OpenAI and ChatGPT integrations out-of-the-box&lt;/li&gt;&#xA;&lt;li&gt;Migrations from v15 that keep TinyMCE in Umbraco as an RTE option by default&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;More information and documentation about TinyMCE.Umbraco here: &lt;a rel=&quot;noopener&quot; href=&quot;https://github.com/ProWorksCorporation/TinyMCE-Umbraco&quot; target=&quot;_blank&quot;&gt;https://github.com/ProWorksCorporation/TinyMCE-Umbraco&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Highlights from the Umbraco 17 Release&lt;/h2&gt;&#xA;&lt;p&gt;Umbraco version 17 includes several major improvements that make it a compelling upgrade for most teams:&lt;/p&gt;&#xA;&lt;h3&gt;Umbraco MCP Server&lt;/h3&gt;&#xA;&lt;p&gt;A new integration layer to enable content editors to report on, manage, create, and update content and media through AI chat interfaces like ChatGPT and Claude. This allows for bulk operations and reporting where AI can reason and help editors understand their content at a new level.&lt;/p&gt;&#xA;&lt;h3&gt;Enhanced caching configuration&lt;/h3&gt;&#xA;&lt;p&gt;The new caching layer allows for more fine-grained configuration. Instead of simply caching all the content, now it can cache only the most used content for faster startup times and better performance for sites with lots of content.&lt;/p&gt;&#xA;&lt;h3&gt;A fully rebuilt back-office&lt;/h3&gt;&#xA;&lt;p&gt;The new back-office UI is powered by standard JavaScript Web Components, delivering better performance, faster load times, and a more modern foundation for custom extensions.&lt;/p&gt;&#xA;&lt;p&gt;Read the official announcement from Umbraco here: &lt;a rel=&quot;noopener&quot; href=&quot;https://umbraco.com/blog/umbraco-17-lts-release/&quot; target=&quot;_blank&quot;&gt;https://umbraco.com/blog/umbraco-17-lts-release/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Thinking about upgrading?&lt;/h2&gt;&#xA;&lt;p&gt;We&#x2019;d be happy to help with:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Upgrading from Umbraco versions 8 - 16 to the latest LTS&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Back-office extension development&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Custom TinyMCE plugins or editor integrations&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Modernizing workflows using MCP Server and AI-enabled tooling&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Custom MCP implementations for Umbraco websites&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If you&#x2019;d like to discuss your upgrade strategy or schedule a brief consultation, &lt;a href=&quot;#section-footer&quot; data-anchor=&quot;#section-footer&quot;&gt;contact us below&lt;/a&gt; and we would be happy to help!&lt;/p&gt;]]></description>
                    <pubDate>Wed, 03 Dec 2025 19:28:36 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@procustomsoftware/top-reasons-to-hire-an-umbraco-development-agency-21ba12473426?source=rss------umbraco-5</guid>
                    <title>Top Reasons to Hire an Umbraco Development Agency</title>
                    <link>https://medium.com/@procustomsoftware/top-reasons-to-hire-an-umbraco-development-agency-21ba12473426?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/top-reasons-to-hire-an-umbraco-development-agency-21ba12473426?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*ELQwyEvMB-I3BHzK_oyyRg.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Choosing the right CMS is one of the most important decisions for any business planning to grow online. Among today&amp;#x2019;s content management&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@procustomsoftware/top-reasons-to-hire-an-umbraco-development-agency-21ba12473426?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Wed, 03 Dec 2025 11:33:46 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/passion-isnt-enough/</guid>
                    <title>When Passion Isn&#x2019;t Enough: Choosing Wellbeing Over the &#x201C;Perfect&#x201D; Job</title>
                    <link>https://24days.in/umbraco-cms/2025/passion-isnt-enough/</link>
                    <description><![CDATA[Even the &#x201C;dream job&#x201D; can become unsustainable when personal wellbeing is compromised. This is a story about recognising burnout, making a tough decision to step away, and rediscovering purpose through reflection, honesty, and community.]]></description>
                    <pubDate>Wed, 03 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/introducing-the-umbraco-sustainability-agent-profile/</guid>
                    <title>Introducing the Umbraco Sustainability Agent Profile</title>
                    <link>https://umbraco.com/blog/introducing-the-umbraco-sustainability-agent-profile/</link>
                    <description><![CDATA[Learn how the new agents.md file helps developers use AI tools more efficiently in Umbraco projects by reducing emissions, saving costs, and supporting sustainable web development.]]></description>
                    <pubDate>Wed, 03 Dec 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/safety-nets-cycling/</guid>
                    <title>Safety Nets: What Cycling Taught Me About Umbraco Development</title>
                    <link>https://24days.in/umbraco-cms/2025/safety-nets-cycling/</link>
                    <description><![CDATA[A story about bikes, brakes, and better development: how unlearning an old &quot;rule&quot; helped me understand Umbraco&#x27;s evolution and how safety nets like monitoring, tests, and community make us braver developers.]]></description>
                    <pubDate>Tue, 02 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/jacksorjacksor.bsky.social/post/3m6x7ojtvfs2t</guid>
                    <title>Umbraco &#x2B; Astro workshop demo upgraded to v17</title>
                    <link>https://bsky.app/profile/jacksorjacksor.bsky.social/post/3m6x7ojtvfs2t</link>
                    <description><![CDATA[First use of Umbraco v17: updating the Umbraco &#x2B; Astro workshop Adam Prendergast and I delivered over the summer!&#xA;&#xA;Having initially built the project on v15 it&#x27;s been great to keep the project with the latest versions of Umbraco, Astro and .NET!&#xA;&#xA;#umbraco #astro&#xA;&#xA;github.com/jacksorjacks...&#xA;&#xA;]]></description>
                    <pubDate>Tue, 02 Dec 2025 07:14:48 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://24days.in/umbraco-cms/2025/creating-lorem-ipsum-generator/</guid>
                    <title>Building My First Umbraco Package: Creating the Lorem Ipsum Generator</title>
                    <link>https://24days.in/umbraco-cms/2025/creating-lorem-ipsum-generator/</link>
                    <description><![CDATA[This article shares my experience of building my first Umbraco package during the Umbraco Spark hackathon. Inspired by an idea from a PM at true, I set out to extend the Rich Text Editor with a Lorem Ipsum dropdown, making it simple to insert placeholder text directly into RTE blocks.]]></description>
                    <pubDate>Mon, 01 Dec 2025 08:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/clean-start-kit-v7-is-out-now-for-umbraco-17/</guid>
                    <title>Clean Start Kit v7 is out now for Umbraco 17</title>
                    <link>https://codeshare.co.uk/blog/clean-start-kit-v7-is-out-now-for-umbraco-17/</link>
                    <description><![CDATA[In this post I talk about the release of Clean v7 for Umbraco 17 and the new automation which has helped me streamline everything to do with maintaining the package.]]></description>
                    <pubDate>Mon, 01 Dec 2025 03:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://mastodon.social/@deanleigh/115620597546182951</guid>
                    <title>Version 17 of UmBootstrap is here</title>
                    <link>https://mastodon.social/@deanleigh/115620597546182951</link>
                    <description><![CDATA[Version 1 7.0.0 of #Umbraco &#x2764;&#xFE0F; and #UmBootstrap &#x1F680;are here: https://umbootstrap.com/]]></description>
                    <pubDate>Fri, 28 Nov 2025 13:47:09 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@aaronsadleruk/115620907295032605</guid>
                    <title>Umbraco v17 versions of UmbNav, uCaptcha, Intl_Tel_Input and Smidge now available</title>
                    <link>https://umbracocommunity.social/@aaronsadleruk/115620907295032605</link>
                    <description><![CDATA[I have released stable versions of my packages for Umbraco 17!UmbNav v4:https://www.nuget.org/packages/Umbraco.Community.UmbNavuCaptcha v6:https://www.nuget.org/packages/Umbraco.Community.Forms.uCaptchaIntl_Tel_Input v6:https://www.nuget.org/packages/Umbraco.Community.Forms.Intl_Tel_InputUmbraco.Community.Smidge v2:https://www.nuget.org/packages/Umbraco.Community.Smidge#umbraco #umbraco17 #launchday]]></description>
                    <pubDate>Fri, 28 Nov 2025 13:46:31 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115621214853488369</guid>
                    <title>Umbraco 17 LTS is here</title>
                    <link>https://umbracocommunity.social/@umbraco/115621214853488369</link>
                    <description><![CDATA[Cue the confetti - Umbraco 17 LTS is here!  &#x1F389;This major-Major version of Umbraco is both a Long-Term Supported version and, for many, includes an upgrade to a modern backoffice architecture (introduced in Umbraco 14). New features and improvements include: - Load Balancing the backoffice  &#x1F4C8;- Consistent handling of dates &#x1F30E;- Runs on the latest Microsoft .NET 10 LTS &#x1F31F;Happy upgrading, everyone! Find out exactly what you get with this release right here: https://umbraco.com/blog/umbraco-17-lts-release/]]></description>
                    <pubDate>Fri, 28 Nov 2025 13:45:20 Z</pubDate>
                        <category>hq</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@lee/115626743762208949</guid>
                    <title>Contentment 6.0.0 For Umbraco 17 out now</title>
                    <link>https://umbracocommunity.social/@lee/115626743762208949</link>
                    <description><![CDATA[#umbraco #Contentment 6.0.0https://github.com/leekelleher/umbraco-contentment/releases/tag/6.0.0TL;DR, fresh out of beta, ready for Umbraco 17/.NET 10.]]></description>
                    <pubDate>Fri, 28 Nov 2025 13:42:31 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=ipHGphfh0DU</guid>
                    <title>umbraCoffee - OP DELIVERED</title>
                    <link>https://www.youtube.com/watch?v=ipHGphfh0DU</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;We&#x27;re joint by two amazing gents from Umbraco HQ - Filip Bech-Larsen &amp; Niels Lyngs&#xF8; to talk about the latest LTS v17 release and more.&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;&#xA;// TODO]]></description>
                    <pubDate>Fri, 28 Nov 2025 09:00:59 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.imagesharpremoteimages</guid>
                    <title>ImageSharpRemoteImages now available for Umbraco v17</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.imagesharpremoteimages</link>
                    <description><![CDATA[Remote Image Provider for ImageSharp | Umbraco Marketplace]]></description>
                    <pubDate>Fri, 28 Nov 2025 08:13:26 Z</pubDate>
                        <category>marketplace</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.fulltextsearch</guid>
                    <title>our.umbraco.fulltextsearch now available for Umbraco v17</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.fulltextsearch</link>
                    <description><![CDATA[Full Text Search | Umbraco Marketplace]]></description>
                    <pubDate>Fri, 28 Nov 2025 07:43:01 Z</pubDate>
                        <category>community</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@abjerner/115623580046543020</guid>
                    <title>Skybrud Redirects is now available for Umbraco 17</title>
                    <link>https://umbracocommunity.social/@abjerner/115623580046543020</link>
                    <description><![CDATA[With #Umbraco 17.0.0 out today, a new release of Skybrud Redirects is now also available &#x1F389;&#x1F984;https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0]]></description>
                    <pubDate>Fri, 28 Nov 2025 05:34:13 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.microsoft</guid>
                    <title>Jumoo.TranslationManager.Microsoft</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.microsoft</link>
                    <description><![CDATA[Microsoft Translation Api Connector for Translation Manager for Umbraco]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.deepl</guid>
                    <title>Jumoo.TranslationManager.DeepL</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.deepl</link>
                    <description><![CDATA[DeepL Translation Api Connector for Translation Manager for Umbraco]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.google</guid>
                    <title>Jumoo.TranslationManager.Google</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.google</link>
                    <description><![CDATA[Google Translation Api Connector for Translation Manager for Umbraco]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.passthrough</guid>
                    <title>Jumoo.TranslationManager.Passthrough</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.passthrough</link>
                    <description><![CDATA[Passes text stright through translation process - good if you just want the language in another site]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.umbnav.core</guid>
                    <title>Umbraco.Community.UmbNav.Core</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.umbnav.core</link>
                    <description><![CDATA[Drag and drop menu editor for Umbraco V15&#x2B;]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/mole.storageproviders.azureblob.temporaryfile</guid>
                    <title>Mole.StorageProviders.AzureBlob.TemporaryFile</title>
                    <link>https://marketplace.umbraco.com/package/mole.storageproviders.azureblob.temporaryfile</link>
                    <description><![CDATA[Azure Blob Storage provider for Umbraco temporary files. Offloads temporary file storage during media uploads and other operations to Azure Blob Storage.]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/jumoo.translationmanager.xliff</guid>
                    <title>Jumoo.TranslationManager.Xliff</title>
                    <link>https://marketplace.umbraco.com/package/jumoo.translationmanager.xliff</link>
                    <description><![CDATA[Xliff 1.2 and Xliff 2.0 Connector for Translation Manager for Umbraco]]></description>
                    <pubDate>Fri, 28 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.0</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Thu, 27 Nov 2025 20:21:47 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=HJ9Y0gdsbPk</guid>
                    <title>Dev Tip: Umbraco Commerce Scaling</title>
                    <link>https://www.youtube.com/watch?v=HJ9Y0gdsbPk</link>
                    <description><![CDATA[Dev Tip: Umbraco Commerce Scaling &#x2013; Bump Digital - YouTube]]></description>
                    <pubDate>Thu, 27 Nov 2025 14:29:28 Z</pubDate>
                        <category>youtube</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://discord.com/channels/882981290662580264/1443601857758101646</guid>
                    <title>Content Lock for Umbraco 17 now available</title>
                    <link>https://discord.com/channels/882981290662580264/1443601857758101646</link>
                    <description><![CDATA[Content Lock for 17&#xA;Only one small new feature - just because it was new in 16.4/17.0&#xA;&#xA; Adds Flag Provider &amp; Entity Sign (To display a locked icon in the tree in realtime for locked nodes)&#xA;&#xA;https://github.com/warrenbuckley/Umbraco.Community.ContentLock/releases/tag/v17.0.0&#xA;https://www.nuget.org/packages/Umbraco.Community.ContentLock/17.0.0&#xA;https://marketplace.umbraco.com/package/umbraco.community.contentlock&#xA;&#xA;/cc @UMB.FYI#3772 tip]]></description>
                    <pubDate>Thu, 27 Nov 2025 13:58:23 Z</pubDate>
                        <category>community</category>
                        <category>discord</category>
                </item>
                <item>
                    <guid>https://github.com/warrenbuckley/Umbraco.Community.ContentLock/releases/tag/v17.0.0</guid>
                    <title>Content Lock for Umbraco 17 out now</title>
                    <link>https://github.com/warrenbuckley/Umbraco.Community.ContentLock/releases/tag/v17.0.0</link>
                    <description><![CDATA[Content Lock for 17&#xA;Only one small new feature - just because it was new in 16.4/17.0&#xA;&#xA; Adds Flag Provider &amp; Entity Sign (To display a locked icon in the tree in realtime for locked nodes)&#xA;&#xA;https://github.com/warrenbuckley/Umbraco.Community.ContentLock/releases/tag/v17.0.0&#xA;https://www.nuget.org/packages/Umbraco.Community.ContentLock/17.0.0&#xA;https://marketplace.umbraco.com/package/umbraco.community.contentlock&#xA;&#xA;/cc @UMB.FYI#3772 tip]]></description>
                    <pubDate>Thu, 27 Nov 2025 13:58:23 Z</pubDate>
                        <category>gihthub</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/MetaMomentum/17.0.0</guid>
                    <title>Meta Momentum V17 out now</title>
                    <link>https://www.nuget.org/packages/MetaMomentum/17.0.0</link>
                    <description><![CDATA[Meta Momentum V17 search and social sharing package has just been released and ready for your upgrades!:v9rocket: &#xA;https://www.nuget.org/packages/MetaMomentum/17.0.0&#xA;https://github.com/DigitalMomentum/MetaMomentum&#xA;&#xA;@UMB.FYI#3772 tip]]></description>
                    <pubDate>Thu, 27 Nov 2025 11:33:08 Z</pubDate>
                        <category>community</category>
                        <category>nuget</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=EfC9jWUaIXk</guid>
                    <title>Unboxing Umbraco 17 LTS</title>
                    <link>https://www.youtube.com/watch?v=EfC9jWUaIXk</link>
                    <description><![CDATA[Join us to learn about the significant improvements and new features of Umbraco 17, the new Long-Term Support release of the CMS now on .NET 10.&#xA;&#xA;We discuss new consistent date handling, user interface enhancements, the introduction of load balancing for the Backoffice and upgrade considerations from previous versions including Models Builder changes.&#xA;Built on .NET 10 (ASP.NET Core 10), Umbraco 17 offers enhanced performance, scalability, and flexibility to meet your project&#x27;s unique needs - whether you&#x27;re developing a simple site or a large-scale content platform.&#xA;&#xA;&#x1F4F0;  Dive into the Umbraco 17 release blog post: https://umbraco.com/blog/umbraco-17-lts-release/&#xA;&#x1F4D6;  Visit our Umbraco 17 product page: https://umbraco.com/products/umbraco-cms/umbraco-17/&#xA;&#x1F517; Ready to learn more? Explore Umbraco 17 documentation: https://docs.umbraco.com/umbraco-cms &#xA;&#xA;&#x1F4D1; Specific documentation links:&#xA;- Version-specific upgrade instructions (including system Date/Time migrations &amp; Models Builder changes): https://docs.umbraco.com/umbraco-cms/fundamentals/setup/upgrading/version-specific&#xA;&#xA;- Umbraco Cloud upgrade instructions: https://docs.umbraco.com/umbraco-cloud/optimize-and-maintain-your-site/manage-product-upgrades/product-upgrades/major-upgrades&#xA;&#xA;- Load balancing the Backoffice: https://docs.umbraco.com/umbraco-cms/17.latest/fundamentals/setup/server-setup/load-balancing/load-balancing-backoffice&#xA;&#xA;00:00 Introduction&#xA;01:36 Umbraco/.NET Release Schedule&#xA;02:20 Highlights tour of Umbraco 14-16&#xA;12:04 Load balancing the Backoffice&#xA;17:15 Consistent handling of dates and new Date/Time property editors&#xA;27:20 UI Improvements including Entity Signs, Entity Action shortcuts, Collection Views, Accessibility improvements&#xA;38:49 Changes in Models Builder&#xA;44:20 Upgrade Considerations&#xA;47:20 What&#x27;s Next&#xA;49:35 Credits]]></description>
                    <pubDate>Thu, 27 Nov 2025 10:27:38 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=AYTO3gAMgUg</guid>
                    <title>Unlocking the Umbraco Management API</title>
                    <link>https://www.youtube.com/watch?v=AYTO3gAMgUg</link>
                    <description><![CDATA[A focused deep-dive into the Umbraco Management API &#x2014; how it really works, how to use it outside the backoffice, and what to watch out for in real implementations. This session covers practical demos, tricky payloads, integration patterns, and common pitfalls to help you push the API further than the docs.&#xA;&#xA;Owain Williams is a Senior Developer and 8&#xD7; Umbraco MVP known for his hands-on expertise and practical guidance across the Umbraco ecosystem.]]></description>
                    <pubDate>Thu, 27 Nov 2025 05:35:16 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.umbnav</guid>
                    <title>Umbraco.Community.UmbNav</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.umbnav</link>
                    <description><![CDATA[Drag and drop menu editor for Umbraco V17&#x2B;]]></description>
                    <pubDate>Thu, 27 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-17-lts-release/</guid>
                    <title>Umbraco 17 LTS: Final Release</title>
                    <link>https://umbraco.com/blog/umbraco-17-lts-release/</link>
                    <description><![CDATA[The latest Umbraco Major version is out. A milestone release that not just offers a bunch of great new features but also 3 years of stability as a Long-Term Supported version]]></description>
                    <pubDate>Thu, 27 Nov 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.forms.intl_tel_input</guid>
                    <title>Umbraco.Community.Forms.Intl_Tel_Input</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.forms.intl_tel_input</link>
                    <description><![CDATA[Add the Intl-Tel-Input text field to your Umbraco Forms for Umbraco V15&#x2B;]]></description>
                    <pubDate>Thu, 27 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.forms.ucaptcha</guid>
                    <title>Umbraco.Community.Forms.uCaptcha</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.forms.ucaptcha</link>
                    <description><![CDATA[A simple to use and integrate captcha plugin for Umbraco Forms which supports hCaptcha, reCaptcha and Cloudflare Turnstile for Umbraco V15&#x2B;]]></description>
                    <pubDate>Thu, 27 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/asyntai.umbraco.chatbot</guid>
                    <title>Asyntai.Umbraco.Chatbot</title>
                    <link>https://marketplace.umbraco.com/package/asyntai.umbraco.chatbot</link>
                    <description><![CDATA[Add an intelligent AI chatbot to your Umbraco website that provides instant answers to your visitors.Your website can now talk.]]></description>
                    <pubDate>Thu, 27 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=4QFesQFZzIo</guid>
                    <title>UK Partner Summit Umbraco 2025</title>
                    <link>https://www.youtube.com/watch?v=4QFesQFZzIo</link>
                    <description><![CDATA[Creating and updating your website should be the least of your worries. With Umbraco, you get a content management system known and loved for its flexibility and great editing experience.&#xA;&#xA;Umbraco is easy to get started with install, set up, and host it yourself or let us take care of it all for you on Umbraco Cloud. https://try.umbraco.com&#xA;&#xA;Learn more about Umbraco on our website https://umbraco.com&#xA;Discover our Umbraco Community: https://community.umbraco.com/&#xA;Find the Umbraco source code on Github: https://github.com/umbraco/Umbraco-CMS&#xA;Visit the Umbraco Documentation: https://docs.umbraco.com]]></description>
                    <pubDate>Wed, 26 Nov 2025 21:48:59 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/secure-member-auth-for-the-delivery-api/</guid>
                    <title>Secure member auth for the Delivery API</title>
                    <link>https://kjac.dev/posts/secure-member-auth-for-the-delivery-api/</link>
                    <description><![CDATA[Securing OpenID Connect auth tokens for publicly facing web apps.]]></description>
                    <pubDate>Wed, 26 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/shazwazza.bsky.social/post/3m6hvcwgfac2n</guid>
                    <title>Articulate for Umbraco is back with Umbraco 17 support</title>
                    <link>https://bsky.app/profile/shazwazza.bsky.social/post/3m6hvcwgfac2n</link>
                    <description><![CDATA[Good news on the horizon - Articulate for #umbraco is back! Thanks to github.com/gavinfaux It&#x27;s working with umb 17 &#x1F389; I&#x27;ve tested exporting my own shazwazza.com blog running umb 12.3 and reimporting with latest code = &#x1F4AF;working. Truly awesome work&#x1F4AA;&#xA;&#xA;]]></description>
                    <pubDate>Tue, 25 Nov 2025 17:54:57 Z</pubDate>
                        <category>bluesky</category>
                        <category>social</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://blog.aabech.no/archive/more-efficient-integration-tests-with-umbraco/</guid>
                    <title>More efficient integration tests with Umbraco</title>
                    <link>https://blog.aabech.no/archive/more-efficient-integration-tests-with-umbraco/</link>
                    <description><![CDATA[&lt;h2&gt;TLDR;&lt;/h2&gt;&#xA;&lt;p&gt;I made &lt;a href=&quot;https://www.nuget.org/packages/Umbraco.Community.Integration.Tests.Extensions&quot;&gt;a package&lt;/a&gt; that removes the &amp;quot;database and Umbraco instance&amp;quot; per fixture constraint from &lt;a href=&quot;https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/&quot;&gt;Umbraco&#x27;s Integration Test library&lt;/a&gt;. It also enables reuse of seeded database snapshots to avoid executing the same setup / seeding over an over per fixture or test.&lt;/p&gt;&#xA;&lt;h2&gt;An itch&lt;/h2&gt;&#xA;&lt;p&gt;I&#x27;ve spent way too long waiting for similar setup code for my integration tests the last couple of years. The modern .NET version of &lt;a href=&quot;https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/&quot;&gt;Umbraco&#x27;s integration test library&lt;/a&gt; is super nice, and real effective with SQLite. But it&#x27;s got a few constraints:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;All things Umbraco is instantiated and spun up per test, granted a configurable amount.&lt;/li&gt;&#xA;&lt;li&gt;Databases have to be &amp;quot;installed&amp;quot; and seeded for each TestFixture at the least.&lt;/li&gt;&#xA;&lt;li&gt;All test fixtures need to inherit from Umbraco&#x27;s base classes&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;So we spend a long time waiting for suff, even if it&#x27;s only the little green box we care about:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1072/initial-umbraco-test-sequence.png&quot; alt=&quot;Diagram of the hierarchy of a basic Umbraco integration test&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;We&#x27;re also stuck either juggling a singleton of some sorts and/or stuffing a lot of &amp;quot;units&amp;quot; into the same fixture.&lt;br /&gt;&#xA;Any notion of intermediate base classes quickly become a sore spot.&lt;/p&gt;&#xA;&lt;h2&gt;An idea&lt;/h2&gt;&#xA;&lt;p&gt;I had recently discovered, likely for the n-th time in my life, that NUnit supports namespace scoped setup fixtures. Those live as long as all that other tests in that namespace and deeper. They bring an opportunity to share initial state across several fixtures. I often find it nice to have several fixtures for the same systems under tests because it makes sense to group by use-case rather than tech.&lt;/p&gt;&#xA;&lt;p&gt;So I set out to see if I could hack together something that made Umbraco&#x27;s base tests possible to use as setup fixtures rather than &amp;quot;base&amp;quot; fixtures. As things would have it it was early october, and the &lt;a href=&quot;https://candidcontributions.com/&quot;&gt;CanCon gang hosted a virtual hackathon&lt;/a&gt; on a friday. I took the day &amp;quot;off&amp;quot; regular work and gave it a go.&lt;/p&gt;&#xA;&lt;h2&gt;Dark magic&lt;/h2&gt;&#xA;&lt;p&gt;In the end I&#x27;ve used all the dirty tricks you can imagine in today&#x27;s .NET landscape. You&#x27;ll find &lt;a href=&quot;https://www.nuget.org/packages/Lib.Harmony&quot;&gt;Lib.Harmony&lt;/a&gt; for messing with Umbraco&#x27;s tests&#x27; IL (Intermediate Language), and there&#x27;s &lt;a href=&quot;https://www.nuget.org/packages/Castle.Core&quot;&gt;Castle DynamicProxy&lt;/a&gt; doing a lot of fooling around with the NUnit test hierarchy. The Harmony bit can likely be removed by making a few slight changes to Umbraco&#x27;s test core, but I wanted to get this working satisfactory before suggesting such changes.&lt;/p&gt;&#xA;&lt;p&gt;The bottom line is that those two tools let us run code before, after or even instead of inherited code. And that in turn enables all the following features.&lt;/p&gt;&#xA;&lt;h2&gt;A new way of composing tests&lt;/h2&gt;&#xA;&lt;p&gt;With the package I&#x27;ve cooked together we can leave the dark world of peach and purple above in favor of a way greener scenery like such:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1071/improved-umbraco-test-sequence.png&quot; alt=&quot;Diagram of the hierarchy of an improved Umbraco integration test suite&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;So without further ado, let&#x27;s dig into how you can get there too.&lt;/p&gt;&#xA;&lt;h2&gt;Umbraco&#x27;s attributes&lt;/h2&gt;&#xA;&lt;p&gt;We rely on Umbraco&#x27;s &lt;code&gt;Tests:Database&lt;/code&gt; config and the &lt;code&gt;[UmbracoTest]&lt;/code&gt; attribute to provide databases (or not). The &lt;code&gt;Database&lt;/code&gt; option works almost as expected, but the four non-none options end up doing the same: they prepare &lt;em&gt;one&lt;/em&gt; db for the lifetime of the setup fixture.&lt;/p&gt;&#xA;&lt;h2&gt;[ExtendableSetUpFixture]&lt;/h2&gt;&#xA;&lt;p&gt;The first attribute you have to know is &lt;code&gt;[ExtendableSetUpFixture]&lt;/code&gt;. It&#x27;s sole purpose is to enable the rest of them. Since it&#x27;s inherited from &lt;code&gt;[SetUpFixture]&lt;/code&gt;, it tells NUnit we won&#x27;t add any &lt;code&gt;[SetUp]&lt;/code&gt;, &lt;code&gt;[TearDown]&lt;/code&gt; or &lt;code&gt;[Test]&lt;/code&gt; methods in the marked class. But this is the attribute we&#x27;ll add to any &lt;code&gt;UmbracoIntegrationTest&lt;/code&gt; or &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt;, and those have a bunch of each. That&#x27;s why we have...&lt;/p&gt;&#xA;&lt;h2&gt;[MakeOneTimeLifecycle]&lt;/h2&gt;&#xA;&lt;p&gt;The first attribute in the package that &lt;em&gt;does something&lt;/em&gt;. &lt;code&gt;[MakeOneTimeLifecycle]&lt;/code&gt; lets you mark methods in otherwise not accessible base classes like &lt;code&gt;UmbracoTestBase&lt;/code&gt; to become &lt;code&gt;[OneTimeSetUp]&lt;/code&gt; methods rather than &lt;code&gt;[SetUp]&lt;/code&gt; methods, and &lt;code&gt;[TearDown]&lt;/code&gt; methods becomes &lt;code&gt;[OneTimeTearDown]&lt;/code&gt;.&lt;br /&gt;&#xA;Here&#x27;s an example of what we&#x27;d feed it for an &lt;code&gt;UmbracoIntegrationTest&lt;/code&gt; inheritor:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[UmbracoTest(Database=SchemaPerFixture, Boot=true)]&#xA;[ExtendableSetUpFixture]&#xA;[MakeOneTimeLifecycle(&#xA;    setUpNames: [nameof(UmbracoIntegrationTest.Setup), nameof(UmbracoIntegrationTest.SetUp_Logging)],&#xA;    tearDownNames: [&#xA;        nameof(UmbracoIntegrationTest.TearDown), nameof(UmbracoIntegrationTest.TearDownAsync), &#xA;        nameof(UmbracoIntegrationTest.FixtureTearDown), nameof(UmbracoIntegrationTest.TearDown_Logging)&#xA;    ]&#xA;)]&#xA;public class MyLongLivedUmbracoSetUp : UmbracoIntegrationTest&#xA;{&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;I found it a bother to write that long attribute all the time, so there&#x27;s a derived one called:&lt;/p&gt;&#xA;&lt;h2&gt;[OneTimeUmbracoSetUp]&lt;/h2&gt;&#xA;&lt;p&gt;It makes it slightly less messy by letting us write as follows:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[UmbracoTest(Database=SchemaPerFixture, Boot=true)]&#xA;[ExtendableSetUpFixture]&#xA;[OneTimeUmbracoSetUp]&#xA;public class MyLongLivedUmbracoSetUp : UmbracoIntegrationTest&#xA;{&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Note however that the current version doesn&#x27;t come with a 100% compatible one for &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt; or &lt;code&gt;ManagementApiTest&amp;lt;&amp;gt;&lt;/code&gt;, but it&#x27;s easy to make one yourself, and they&#x27;re likely suddenly in the package.&lt;/p&gt;&#xA;&lt;p&gt;Now if you&#x27;re into hacking around the environment and hidden, protected members lurking around in memory, we&#x27;re quite alike. But when doing professional work that&#x27;s often not what we&#x27;re paid for. So even though the above code will start up a perfectly fine Umbraco instance with a SQLite database it doesn&#x27;t do much good. We need to get at the &lt;code&gt;IServiceProvider&lt;/code&gt; somehow...&lt;/p&gt;&#xA;&lt;h2&gt;[InjectionProvider(string)] and [ServiceProvider]&lt;/h2&gt;&#xA;&lt;p&gt;There is a limit to how many attributes we&#x27;ll stick on our setup fixtures, I promise. I complained about intermediate base classes earlier, but a few for attribute consolidation is cool enough. OK, these two are a basic re-usable one and a &amp;quot;defaulted&amp;quot; one. The &lt;code&gt;[InjectionProvider(string)]&lt;/code&gt; attribute stores a reference to a property on the setup fixture that exposes an &lt;code&gt;IServiceProvider&lt;/code&gt; instance. Umbraco&#x27;s base classes exposea property called &lt;code&gt;Services&lt;/code&gt;, and &lt;code&gt;[ServiceProvider]&lt;/code&gt; is just a derived &lt;code&gt;[InjectionProvider(nameof(Services))]&lt;/code&gt;. To be specific, the service provider reference goes into the test context property bag as a factory method.&lt;br /&gt;&#xA;By adding one to our growing tower we&#x27;re ready to meet our first actual &lt;em&gt;test fixture&lt;/em&gt; enjoying the power of:&lt;/p&gt;&#xA;&lt;h2&gt;[Inject(string)]&lt;/h2&gt;&#xA;&lt;p&gt;To enjoy the benefits of our stored service provider reference, we must expose an instance method on our &amp;quot;scoped&amp;quot; test fixture that accepts services. We could&#x27;ve used constructor injection, but that&#x27;s already &amp;quot;in use&amp;quot; by NUnit &lt;code&gt;[TestFixture(args)]&lt;/code&gt; functionality. Once you start composing tests like this package allows, you can get &lt;em&gt;much more value&lt;/em&gt; from things like that. So a void method will have to do. A fresh &amp;quot;standalone&amp;quot; and &amp;quot;scoped&amp;quot; test fixture will look like this:&lt;/p&gt;&#xA;&lt;p&gt;using Same.Namespace.As.SetupFixture;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[Inject(nameof(Inject))]&#xA;public class LookMaImFreeAsABird&#xA;{&#xA;    IDataTypeService dataTypeService = null!;&#xA;&#xA;    public void Inject(IDataTypeService dataTypeService)&#xA;    {&#xA;        this.dataTypeService = dataTypeService;&#xA;    }&#xA;&#xA;    [Test]&#xA;    public void FiddleWithDataTypeA()&#xA;    {&#xA;        // ...&#xA;    }&#xA;&#xA;    [Test]&#xA;    public void CreateDataTypeB()&#xA;    {&#xA;        // ...&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Notice the beauty of &lt;em&gt;no forced ctors&lt;/em&gt;, &lt;em&gt;no base class&lt;/em&gt; and &lt;em&gt;no overrides&lt;/em&gt; in that class! Not to mention, you can have &lt;em&gt;several&lt;/em&gt; of those!&lt;/p&gt;&#xA;&lt;h2&gt;Interlude: Transactions&lt;/h2&gt;&#xA;&lt;p&gt;With the above setup the individual fixtures are free to create &lt;code&gt;ICoreScope&lt;/code&gt; instances from Umbraco and commit or dispose them as you wish. Just writing three sentences about it here doesn&#x27;t really give the method justice. Suffice to say, just give it a go!&lt;br /&gt;&#xA;However it won&#x27;t work when we use &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt;. There&#x27;s no way to spread a core scope across threads, and there&#x27;s not even a way to spread gool old .NET transaction scopes across. I guess you could go full old school and bring DCOM into the picture, but after a lot of failed attempts I finally stumbled over an obvious solution.&lt;/p&gt;&#xA;&lt;h2&gt;Snapshots&lt;/h2&gt;&#xA;&lt;p&gt;The final itch has taken me the longest, and even longer to stuff into an attribute rather than yet another base class. I must admit it took a desperate (but accurate) final plea with ChatGPT 5.1 to see the obvious solution just lying there to implement. All database engines, or at least the two I&#x27;ve implemented, support some sort of fast backup. &lt;code&gt;VACUUM INTO&lt;/code&gt; in SQLite and &lt;code&gt;CREATE/RESTORE DATABASE AS SNAPSHOT&lt;/code&gt; in SQL Server.&lt;/p&gt;&#xA;&lt;p&gt;I had two intermediate base classes for a few weeks, but this last weekend I ran over the goal line:&lt;/p&gt;&#xA;&lt;h2&gt;&lt;code&gt;[ReusableDatabase(type, string)]&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;p&gt;If you configure &lt;code&gt;Tests:Database:DatabaseType&lt;/code&gt; in your &lt;code&gt;appSettings.Tests.Local.json&lt;/code&gt; and set any of the non-none values in the &lt;code&gt;[UmbracoTest]&lt;/code&gt; database parameter, you can also add &lt;code&gt;[ReusableDatabase(typeof(SeedClass), nameof(SeedClass.Configure))]&lt;/code&gt; to get a new type of &lt;code&gt;ITestDatabase&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;As of writing it requires a method to be implemented in the setup fixture, or on some supporting type. Its mission is to configure whether the database needs a fresh seed and how to seed the initial data for all the tests. Only if we say so, the database is installed and re-seeded. For all other scopes it&#x27;s just restored from that initial snapshot.&lt;/p&gt;&#xA;&lt;p&gt;The seeding can be done in any way you please. My favorite is importing things using uSync, and that might just become another blog post. For simplicity let&#x27;s say we&#x27;re testing variants of property editors based on a datatype we want all tests to start with:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;[UmbracoTest(&#xA;    Database = UmbracoTestOptions.Database.NewSchemaPerTest,&#xA;    Boot = true,&#xA;    Logger = UmbracoTestOptions.Logger.Console&#xA;)]&#xA;[ExtendableSetUpFixture]&#xA;[OneTimeUmbracoSetUp]&#xA;[ServiceProvider]&#xA;[ReusableDatabase(nameof(ConfigureSeeding))]&#xA;public class ReusedDbAttributeSetUp : UmbracoIntegrationTest&#xA;{&#xA;    public static void ConfigureSeeding(ReusableTestDatabaseOptions options)&#xA;    {&#xA;        options.NeedsNewSeed = _ =&amp;gt; Task.FromResult(true);&#xA;        options.SeedData = async (services) =&amp;gt;&#xA;        {&#xA;            await TestContext.Progress.WriteLineAsync(&amp;quot;Creating datatype&amp;quot;);&#xA;            await services.GetRequiredService&amp;lt;IDataTypeService&amp;gt;().CreateAsync(&#xA;                new DataType&#xA;                (&#xA;                    new TextboxPropertyEditor&#xA;                    (&#xA;                        services.GetRequiredService&amp;lt;IDataValueEditorFactory&amp;gt;(),&#xA;                        services.GetRequiredService&amp;lt;IIOHelper&amp;gt;()&#xA;                    ),&#xA;                    services.GetRequiredService&amp;lt;IConfigurationEditorJsonSerializer&amp;gt;()&#xA;                )&#xA;                {&#xA;                    Name = &amp;quot;A seeded textbox&amp;quot;&#xA;                },&#xA;                Umbraco.Cms.Core.Constants.Security.SuperUserKey&#xA;            );&#xA;        };&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;The method has to be static void. If it&#x27;s on the setup fixture you can omit the type from the attribute parameters.&lt;br /&gt;&#xA;Now all tests within this &amp;quot;scope&amp;quot; will have access to the seeded textbox. Such seeding code might quickly grow out of hand though, and that&#x27;s why I prefer just importing the content schema from the web project in the same repo as my tests.&lt;/p&gt;&#xA;&lt;h2&gt;Which ones to choose&lt;/h2&gt;&#xA;&lt;p&gt;As you&#x27;ve hopefully realized by now you can introduce really slim intermediate base classes that set up the initial environment for a bunch of use-cases and scenarios. Leaving your actual test fixtures free to be composed just as you wish, and with all the features of NUnit at your disposal.&lt;/p&gt;&#xA;&lt;p&gt;I&#x27;m sure you can see that the last setup fixture example above makes for a nice base class to avoid repeating that tower of attributes. The &lt;a href=&quot;https://github.com/lars-erik/NUnitCompositionWithUmbraco/tree/main/UmbracoTestsComposition&quot;&gt;repository has a few examples using a few or all of the attributes&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The final trick I haven&#x27;t disclosed above is how to do a &amp;quot;mid-fixture&amp;quot; rollback. The reusable database implementations have a &amp;quot;RestoreSnapshot&amp;quot; method, so in a &lt;code&gt;[TearDown]&lt;/code&gt; you can go:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;await serviceProvider.GetRequiredService&amp;lt;IReusableDatabase&amp;gt;().RestoreSnapshot();&lt;/code&gt; &lt;/p&gt;&#xA;&lt;p&gt;That makes it so that whatever test comes next it&#x27;s unaffected by the changes you&#x27;ve made. This is of course what happens during &lt;code&gt;[OneTimeSetUp]&lt;/code&gt; for any setup fixture using &lt;code&gt;[ReusableDatabase]&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If you want to try this out, I recommend going with &lt;code&gt;[ReusableDatabase]&lt;/code&gt; (and uSync) for everything that use more than 10ish artifacts like data and content types, not to mention content. Whenever you want to run changes through the Management API, or definitely if you want to test how all your sites&#x27; elements look in the Delivery API. It&#x27;s a no-brainer when you go full integration with &lt;code&gt;UmbracoTestServerTestBase&lt;/code&gt; and more than one HTTP call.&lt;/p&gt;&#xA;&lt;p&gt;If all your tests have all they need in a fresh Umbraco database, but they all mutate it so you need cleanup, it&#x27;ll be blazing fast if you can use &lt;code&gt;ICoreScope&lt;/code&gt; with autocommit off. As long as you don&#x27;t need to test via HTTP endpoints, this is a better option than restoring the snapshot. Using a snapshot restore between fixtures however is still a nice option to keep around, so I still opt for &lt;code&gt;[ReusableDatabase]&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2&gt;Want more?&lt;/h2&gt;&#xA;&lt;p&gt;I&#x27;m definitely going to be in &amp;quot;only fix what breaks for me&amp;quot; mode for a little while, as this was just gonna be a hacktoberfest weekend project. Turned out to take too much of my spare time for one and a half month instead.&lt;/p&gt;&#xA;&lt;p&gt;But I honestly believe this&#x27;ll save some trees if applied well to CI pipelines running all day long.&lt;br /&gt;&#xA;And I&#x27;ve gone from 90 sec to 26 sec to execute a full test suite, a lot of which would run for &lt;em&gt;any&lt;/em&gt; filter.&lt;/p&gt;&#xA;&lt;p&gt;The package is currently listed as a beta (pre-release) on nuget and if it doesn&#x27;t crash much I might just promote it to full visibility. It&#x27;s a bit furry on the edges and has a bit of legacy to it (already), but if you stick the namespaces in the project file and leave the stray base classes alone I think you&#x27;ll be good.&lt;/p&gt;&#xA;&lt;p&gt;All this is to say I&#x27;d love for conversation about what it solves and if it could do it better. I&#x27;d love even more to throw out a bit of the code because Umbraco suddenly fixed the core two issues. Let me hear from you if you try it out. Feel free to clone the code and mess about, although I reserve the right to refactor the mess myself that beautiful day I get to keep going with this. &lt;/p&gt;&#xA;&lt;p&gt;Looking forward to hearing how (and if) you like it. Hope you have a go!&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://github.com/lars-erik/NUnitCompositionWithUmbraco&quot;&gt;Source repository&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Umbraco.Community.Integration.Tests.Extensions&quot;&gt;Nuget package&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/Umbraco.Cms.Tests.Integration/&quot;&gt;Umbraco Cms Tests Integration package&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;]]></description>
                    <pubDate>Tue, 25 Nov 2025 07:11:08 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/bellissima-backoffice-property-descriptions/</guid>
                    <title>Bellissima Backoffice: Property Descriptions</title>
                    <link>https://joe.gl/ombek/blog/bellissima-backoffice-property-descriptions/</link>
                    <description><![CDATA[&lt;p&gt;I&#x27;m sure you&#x27;re already aware of property descriptions! They&#x2019;re useful to explain a property when a name doesn&#x27;t give enough detail. The property descriptions have accepted markdown for a long time now (although allowed features have changed over time!). And because it&#x2019;s markdown, that means we can use HTML too. And because its HTML it means we can also make use of web components, like UUI!&lt;/p&gt;&#xA;&lt;h3&gt;Property Descriptions: Read more&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;One of the features that&#x27;s changed in recent versions is the &quot;read more&quot; functionality. In earlier versions of Umbraco, &lt;code&gt;---&lt;/code&gt; rendered a read more link.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Short description&#xA;&#xA;---&#xA;&#xA;Descriptions below a `---` were rendered behind a &quot;Read More&quot; link in v9-13.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This is no longer the case, &lt;code&gt;---&lt;/code&gt; renders a horizontal rule element (as it should!) but we can recreate the read more functionality with modern HTML.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Short description&#xA;&#xA;---&#xA;&#xA;Three dashes renders a horizontal rule.&#xA;&#xA;&amp;lt;details&amp;gt;&#xA;&amp;lt;summary&amp;gt;Read more&amp;lt;/summary&amp;gt;&#xA;&#xA;We have to use the native HTML `details` element for modern Umbraco.&#xA;&#xA;&amp;lt;/details&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;We can even take that a step further - in this example I&#x2019;m using: markdown to render italic text, a code sample and a hyperlink; HTML to render a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element to recreate the read more functionality; and because UUI uses web components, which are HTML, we can bring UUI elements into the descriptions too, so I&#x2019;ve got a UUI Umbraco help icon in there too to make the property description rich and intuitive without taking too much space.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Should search engines and other crawlers index this page and serve them up as search results?&#xA;&amp;lt;details&amp;gt;&#xA;&amp;lt;summary&amp;gt;&#xA;  &amp;lt;uui-icon name=&quot;icon-help-alt&quot; label=&quot;More details&quot;&amp;gt;&amp;lt;/uui-icon&amp;gt; &#xA;&amp;lt;/summary&amp;gt;&#xA;&#xA;This sets the *index* aspect of the [`robots` meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/robots)&#xA;&#xA;&amp;lt;/details&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/csahpboa/prop-description.png&quot; alt=&quot;A screen capture of the markdown rendered in the backoffice with a user clicking on the question-mark help icon to expand further details.&quot; /&gt;&lt;/p&gt;]]></description>
                    <pubDate>Mon, 24 Nov 2025 07:15:24 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/belissima-backoffice-property-descriptions/</guid>
                    <title>Belissima Backoffice: Property Descriptions</title>
                    <link>https://joe.gl/ombek/blog/belissima-backoffice-property-descriptions/</link>
                    <description><![CDATA[&lt;p&gt;I&#x27;m sure you&#x27;re already aware of property descriptions! They&#x2019;re useful to explain a property when a name doesn&#x27;t give enough detail. The property descriptions have accepted markdown for a long time now (although allowed features have changed over time!). And because it&#x2019;s markdown, that means we can use HTML too. And because its HTML it means we can also make use of web components, like UUI!&lt;/p&gt;&#xA;&lt;h3&gt;Property Descriptions: Read more&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;One of the features that&#x27;s changed in recent versions is the &quot;read more&quot; functionality. In earlier versions of Umbraco, &lt;code&gt;---&lt;/code&gt; rendered a read more link.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Short description&#xA;&#xA;---&#xA;&#xA;Descriptions below a `---` were rendered behind a &quot;Read More&quot; link in v9-13.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This is no longer the case, &lt;code&gt;---&lt;/code&gt; renders a horizontal rule element (as it should!) but we can recreate the read more functionality with modern HTML.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Short description&#xA;&#xA;---&#xA;&#xA;Three dashes renders a horizontal rule.&#xA;&#xA;&amp;lt;details&amp;gt;&#xA;&amp;lt;summary&amp;gt;Read more&amp;lt;/summary&amp;gt;&#xA;&#xA;We have to use the native HTML `details` element for modern Umbraco.&#xA;&#xA;&amp;lt;/details&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;We can even take that a step further - in this example I&#x2019;m using: markdown to render italic text, a code sample and a hyperlink; HTML to render a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element to recreate the read more functionality; and because UUI uses web components, which are HTML, we can bring UUI elements into the descriptions too, so I&#x2019;ve got a UUI Umbraco help icon in there too to make the property description rich and intuitive without taking too much space.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;Should search engines and other crawlers index this page and serve them up as search results?&#xA;&amp;lt;details&amp;gt;&#xA;&amp;lt;summary&amp;gt;&#xA;  &amp;lt;uui-icon name=&quot;icon-help-alt&quot; label=&quot;More details&quot;&amp;gt;&amp;lt;/uui-icon&amp;gt; &#xA;&amp;lt;/summary&amp;gt;&#xA;&#xA;This sets the *index* aspect of the [`robots` meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/robots)&#xA;&#xA;&amp;lt;/details&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&quot;/media/csahpboa/prop-description.png&quot; alt=&quot;A screen capture of the markdown rendered in the backoffice with a user clicking on the question-mark help icon to expand further details.&quot; /&gt;&lt;/p&gt;]]></description>
                    <pubDate>Mon, 24 Nov 2025 07:15:24 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/jasonelkin.uk/post/3m656wgo4j22u</guid>
                    <title>Protecting an Umbraco site against a bot attack</title>
                    <link>https://bsky.app/profile/jasonelkin.uk/post/3m656wgo4j22u</link>
                    <description><![CDATA[A client&#x27;s #Umbraco site was recently taken down by a bot. Here&#x27;s a post about what happened, and what we did to mitigate the attack.&#xA;&#xA;If you&#x27;ve got an Umbraco site with forms (Umbraco Forms, or surface controllers) you probably need this WAF rule. &#xA;&#xA;dev.to/jasonelkin/t...&#xA;&#xA;]]></description>
                    <pubDate>Fri, 21 Nov 2025 11:46:03 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://dev.to/jasonelkin/the-bot-that-shouldnt-have-taken-down-my-umbraco-site-and-the-waf-rule-that-fixed-it-36o6</guid>
                    <title>The Bot That Shouldn&#x2019;t Have Taken Down My Umbraco Site, and the WAF Rule That Fixed It</title>
                    <link>https://dev.to/jasonelkin/the-bot-that-shouldnt-have-taken-down-my-umbraco-site-and-the-waf-rule-that-fixed-it-36o6</link>
                    <description><![CDATA[&lt;p&gt;This week, one of our sites was brought down by a Denial of Service attack. It wasn&#x2019;t really a DoS attack, i.e. I&#x2019;m sure that wasn&#x2019;t the attackers&#x2019; aim, but the attack did cause a Denial of Service nonetheless.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It was clear a bot was spamming the site with several thousands of requests per minute, but that in itself was well within the threshold that we&#x2019;d expect the site to be able to handle - this is a modern Umbraco site with load-balancing/auto-scaling and a well optimized codebase after all.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  So, &lt;em&gt;what&lt;/em&gt; was going on?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The problem was the nature of the traffic &#x2013; form submissions, shedloads of form submissions being made to both Umbraco forms and our custom surface controllers - that were causing errors.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It was those errors that contributed to a significant degradation in performance, leaving us with come challenges that we had to find a way to overcome:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  1. Umbraco Forms&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Umbraco Forms has a honeypot field built-in &#x2013; this is a hidden text field that looks tempting to bots, so they fill it in. If a form is submitted with a value in that field, Umbraco Forms will reject the submission. The bot hitting our site was totally falling for the honeypot - great! So what&#x2019;s the problem? When Umbraco Forms rejects that submission it also logs that rejection.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Logging is a non-trivial concern in performance-critical applications. In Umbraco, every log entry is a disk write operation by default. So, although the site would normally be able to handle this level of traffic easily, these POST requests are resulting in a disk write (which in an Azure Web App is actually a network request under the hood) &#x2013; gobbling up IO and slowing the site down.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  2. Surface controllers&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;We had a bigger problem with our surface controllers. Surface controllers &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/routing/surface-controllers/#protecting-surface-controller-routes&quot; rel=&quot;noopener noreferrer&quot;&gt;use a hidden &lt;code&gt;ufprt&lt;/code&gt; field&lt;/a&gt; to store an encrypted value for routing form submissions. If there&#x2019;s a problem with this field, i.e. Umbraco can&#x2019;t decrypt it, an error is thrown. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;The bot spamming us was stuffing that field with SQL in the vain hope of carrying out an SQL Injection. Of course Umbraco was unable to decrypt that into a valid &lt;code&gt;ufprt&lt;/code&gt; value so threw an error.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, just like with Umbraco Forms we have an error being logged for each of these requests, with the added overhead of an Exception being thrown.&lt;/p&gt;&#xA;&#xA;&lt;h4&gt;&#xA;  &#xA;  &#xA;  2.1 reCAPTCHA&#xA;&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;There was another problem with our surface controllers: reCAPTCHA.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Like a lot of other .NET projects, we use an attribute on our controllers to handle reCAPTCHA validation. The problem is that this validation was running before Umbraco tried to decrypt the ufprt field &#x2013; so as well as the exception and extra writes to disk, every request was also making an HTTP request to the reCAPTCHA API, gobbling up precious TCP threads.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  We&#x27;re serving all these requests!&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;On top of the errors themselves, we&#x2019;re then returning a 500 error page for most of these requests.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;These are dynamic/server-rendered pages on this site so they are using compute &#x2013; it&#x2019;s not a big deal, but we don&#x2019;t want to be wasting those resources on bot requests.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Considerations&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Sure, we could look at each of the individual problems above and solve for them but&#x2026;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I still want to know about errors, so I need logging.&lt;/li&gt;&#xA;&lt;li&gt;I still want to use reCAPTCHA to validate form submissions, especially from bots.&lt;/li&gt;&#xA;&lt;li&gt;I still want to serve dynamic error pages (though there&#x2019;s an argument to be made for better caching).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;For legitimate traffic, I still want all of this to happen. For illegitimate traffic I don&#x2019;t want our app to be responding at all - it&#x2019;s a waste of resources. In fact, W3C&#x2019;s Web Sustainability Guidelines recommend &lt;a href=&quot;https://w3c.github.io/sustainableweb-wsg/#use-automation-wisely&quot; rel=&quot;noopener noreferrer&quot;&gt;filtering suspicious activity&lt;/a&gt; for this reason.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;We should be blocking this traffic at the edge, and that&#x2019;s a job for the WAF - In this case Cloudflare.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Solution&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;It&#x2019;s time for a new WAF rule.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Fortunately, there&#x2019;s a really simple rule that we can deploy in Cloudflare to block this traffic.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I set up a new &lt;a href=&quot;https://developers.cloudflare.com/waf/rate-limiting-rules/&quot; rel=&quot;noopener noreferrer&quot;&gt;Rate Limiting Rule&lt;/a&gt; with an expression that matches all POST requests, then rate limited it to 25 POST requests per IP address every 10 seconds before issuing a challenge.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;No legitimate user is going to be making more than 20 POST requests in 20 seconds. You might even think that&#x2019;s a little high, but we can only rate-limit by IP address, and multiple users will legitimately be sharing a single IP.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Here&#x2019;s what that looks like in the portal:&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8wf0rxujbta0fbgztxa.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8wf0rxujbta0fbgztxa.png&quot; alt=&quot;A screenshot of the Cloudflare rate limiting rule UI&quot; width=&quot;747&quot; height=&quot;1167&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Result&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;3 million requests have been blocked since I deployed that rule yesterday and, most importantly, the site has remained online and performant for legitimate users throughout.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcamqqb4jj6sn0n7m11h.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcamqqb4jj6sn0n7m11h.png&quot; alt=&quot;A summary from the Cloudflare dashboard showing 3 Million blocked requests in the last 24 hours&quot; width=&quot;800&quot; height=&quot;94&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Win.&lt;/p&gt;]]></description>
                    <pubDate>Fri, 21 Nov 2025 11:19:36 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=r3EYipUWEPc</guid>
                    <title>In Conversation With CMS Critic</title>
                    <link>https://www.youtube.com/watch?v=r3EYipUWEPc</link>
                    <description><![CDATA[Creating and updating your website should be the least of your worries. With Umbraco, you get a content management system known and loved for its flexibility and great editing experience.&#xA;&#xA;Umbraco is easy to get started with install, set up, and host it yourself or let us take care of it all for you on Umbraco Cloud. https://try.umbraco.com&#xA;&#xA;Learn more about Umbraco on our website https://umbraco.com&#xA;Discover our Umbraco Community: https://community.umbraco.com/&#xA;Find the Umbraco source code on Github: https://github.com/umbraco/Umbraco-CMS&#xA;Visit the Umbraco Documentation: https://docs.umbraco.com]]></description>
                    <pubDate>Fri, 21 Nov 2025 09:07:47 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/shorts/c-2YOI8xFOE</guid>
                    <title>Is AI going to take our jobs?</title>
                    <link>https://www.youtube.com/shorts/c-2YOI8xFOE</link>
                    <description><![CDATA[Final call for attendees&#xA;&#xA;The ClerksWell Charity Hackathon is happening on 28th November in London &#xA;&#xA;If you can get to London on that day, please sign up to our free event&#xA;&#xA;https://clerkswell.com/hackathon25&#xA;&#xA;FAQs&#xA; &#xA;&#x1F193; Is it free to enter?&#xA;Yes the hackathon is completely free to enter, you just need to make your way to London for the day. Food and drink will also be provided. If you live outside of London and travel costs are preventing you from attending please get in touch as we may be able to help you.&#xA; &#xA;&#x1F3C6; What are the prizes?&#xA;There will be two winning teams, one for each of the two charity challenges.&#xA;Each team will consist of 2 people.&#xA;The prize for each winning team is:&#xA;1 Codegarden ticket&#xA;1 Expert Training &amp; Certification bundle&#xA;If your team wins, you&#x2019;ll need to decide between yourselves who gets which prize.&#xA; &#xA;&#x1F4BB; How much Umbraco do we need to use?&#xA;There are no strict limits, just make sure you use Umbraco 17 in some way. It doesn&#x2019;t need to be a full-blown implementation. Be creative!&#xA; &#xA;&#x23F1; What if we can&#x27;t finish a working solution in time?&#xA;That&#x2019;s totally fine. The judges will assess entries based on several criteria, including usability and how well the idea meets the brief. Even if your solution isn&#x2019;t fully working, your concept and approach still count.&#xA; &#xA;&#x1F465; Do I need to already have a teammate?&#xA;No. We&#x2019;ve shared a private Slack channel with attendees where you can find a partner and form a team.&#xA; &#xA;&#x1F9E0; How good do I need to be at coding?&#xA;You don&#x2019;t need to be an expert. If you team up with someone whose skills complement yours, you&#x2019;ll be in a great position. For example, if you&#x27;re stronger in front-end or UX, you could pair with someone who&#x2019;s more backend-focused.&#xA;There will also be people around to help if you get stuck.&#xA; &#xA;&#x1F552; What are the timings? What if I arrive late or leave early?&#xA;Coding starts at 10:00 and ends at 16:00.&#xA;If travel is a challenge, it&#x2019;s okay to arrive a bit late or leave slightly early. Just make sure you&#x2019;ve coordinated with your teammate in advance so you&#x2019;re aligned on what you&#x2019;ll be working on.&#xA; &#xA;&#x1F310; Can I attend online?&#xA;This is an in-person event only.&#xA;However, the challenge briefs are public, and you&#x2019;re welcome to try them out on your own. If you&#x2019;d like to share your work with the charities afterwards, we&#x2019;d be happy to connect you.&#xA; &#xA;&#x1FAF1; Can I take part if I already know the charities or ClerksWell?&#xA;Yes, that&#x2019;s not a problem. There are six independent judges, and ClerksWell won&#x2019;t be involved in judging, to keep things fair.&#xA; &#xA;&#x2764;&#xFE0F; Are the charities connected to ClerksWell?&#xA;The charities are not clients of ClerksWell, but they are close to our hearts:&#xA;Headway was suggested by Paul, who previously worked at an agency that built their website. Several of Paul&#x2019;s family members have experienced brain injuries and benefited from Headway&#x2019;s services.&#xA;Restore Hope was suggested by Marc from ClerksWell, who has volunteered with them and admires the work they do in his local area.]]></description>
                    <pubDate>Thu, 20 Nov 2025 16:53:24 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/automate-your-lead-flow-in-umbraco-with-webhooks-and-zapier/</guid>
                    <title>Automate Your Lead Flow in Umbraco with Webhooks and Zapier</title>
                    <link>https://umbraco.com/blog/automate-your-lead-flow-in-umbraco-with-webhooks-and-zapier/</link>
                    <description><![CDATA[From form submit to CRM: Learn how to automate lead validation, enrichment, and routing with Umbraco Forms, Zapier, and a bit of AI magic.]]></description>
                    <pubDate>Thu, 20 Nov 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0-beta002</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.0-beta002</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0-beta002</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Wed, 19 Nov 2025 22:44:11 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v16.0.2</guid>
                    <title>Skybrud.Umbraco.Redirects 16.0.2</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v16.0.2</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Wed, 19 Nov 2025 22:26:02 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/shorts/i5MIxUMbxwI</guid>
                    <title>I love working on proof of concepts, don&#x2019;t you?</title>
                    <link>https://www.youtube.com/shorts/i5MIxUMbxwI</link>
                    <description><![CDATA[The ClerksWell Charity Hackathon is happening on 28th November in London &#xA;&#xA;If you can get to London on that day, please sign up to our free event&#xA;&#xA;https://clerkswell.com/hackathon25&#xA;&#xA;FAQs&#xA; &#xA;&#x1F193; Is it free to enter?&#xA;Yes the hackathon is completely free to enter, you just need to make your way to London for the day. Food and drink will also be provided. If you live outside of London and travel costs are preventing you from attending please get in touch as we may be able to help you.&#xA; &#xA;&#x1F3C6; What are the prizes?&#xA;There will be two winning teams, one for each of the two charity challenges.&#xA;Each team will consist of 2 people.&#xA;The prize for each winning team is:&#xA;1 Codegarden ticket&#xA;1 Expert Training &amp; Certification bundle&#xA;If your team wins, you&#x2019;ll need to decide between yourselves who gets which prize.&#xA; &#xA;&#x1F4BB; How much Umbraco do we need to use?&#xA;There are no strict limits, just make sure you use Umbraco 17 in some way. It doesn&#x2019;t need to be a full-blown implementation. Be creative!&#xA; &#xA;&#x23F1; What if we can&#x27;t finish a working solution in time?&#xA;That&#x2019;s totally fine. The judges will assess entries based on several criteria, including usability and how well the idea meets the brief. Even if your solution isn&#x2019;t fully working, your concept and approach still count.&#xA; &#xA;&#x1F465; Do I need to already have a teammate?&#xA;No. We&#x2019;ve shared a private Slack channel with attendees where you can find a partner and form a team.&#xA; &#xA;&#x1F9E0; How good do I need to be at coding?&#xA;You don&#x2019;t need to be an expert. If you team up with someone whose skills complement yours, you&#x2019;ll be in a great position. For example, if you&#x27;re stronger in front-end or UX, you could pair with someone who&#x2019;s more backend-focused.&#xA;There will also be people around to help if you get stuck.&#xA; &#xA;&#x1F552; What are the timings? What if I arrive late or leave early?&#xA;Coding starts at 10:00 and ends at 16:00.&#xA;If travel is a challenge, it&#x2019;s okay to arrive a bit late or leave slightly early. Just make sure you&#x2019;ve coordinated with your teammate in advance so you&#x2019;re aligned on what you&#x2019;ll be working on.&#xA; &#xA;&#x1F310; Can I attend online?&#xA;This is an in-person event only.&#xA;However, the challenge briefs are public, and you&#x2019;re welcome to try them out on your own. If you&#x2019;d like to share your work with the charities afterwards, we&#x2019;d be happy to connect you.&#xA; &#xA;&#x1FAF1; Can I take part if I already know the charities or ClerksWell?&#xA;Yes, that&#x2019;s not a problem. There are six independent judges, and ClerksWell won&#x2019;t be involved in judging, to keep things fair.&#xA; &#xA;&#x2764;&#xFE0F; Are the charities connected to ClerksWell?&#xA;The charities are not clients of ClerksWell, but they are close to our hearts:&#xA;Headway was suggested by Paul, who previously worked at an agency that built their website. Several of Paul&#x2019;s family members have experienced brain injuries and benefited from Headway&#x2019;s services.&#xA;Restore Hope was suggested by Marc from ClerksWell, who has volunteered with them and admires the work they do in his local area.&#xA;&#xA;Let me know if you have any others to add or want to change any. We are checking with Lotte about the one which mentions help with travel, so please don&#x27;t publish yet.]]></description>
                    <pubDate>Wed, 19 Nov 2025 16:10:11 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=vhHxvXhA738</guid>
                    <title>US Partner Summit Umbraco 2025</title>
                    <link>https://www.youtube.com/watch?v=vhHxvXhA738</link>
                    <description><![CDATA[Creating and updating your website should be the least of your worries. With Umbraco, you get a content management system known and loved for its flexibility and great editing experience.&#xA;&#xA;Umbraco is easy to get started with install, set up, and host it yourself or let us take care of it all for you on Umbraco Cloud. https://try.umbraco.com&#xA;&#xA;Learn more about Umbraco on our website https://umbraco.com&#xA;Discover our Umbraco Community: https://community.umbraco.com/&#xA;Find the Umbraco source code on Github: https://github.com/umbraco/Umbraco-CMS&#xA;Visit the Umbraco Documentation: https://docs.umbraco.com]]></description>
                    <pubDate>Wed, 19 Nov 2025 14:24:47 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-cms-developer-mcp-for-umbraco-16/</guid>
                    <title>Umbraco CMS Developer MCP for Umbraco 16</title>
                    <link>https://umbraco.com/blog/umbraco-cms-developer-mcp-for-umbraco-16/</link>
                    <description><![CDATA[Two months ago, we released the beta of the Umbraco MCP Server - a bridge between AI assistants and Umbraco CMS. Now, we&#x27;re thrilled to announce the official first release of the Umbraco CMS Developer MCP for Umbraco 16.]]></description>
                    <pubDate>Wed, 19 Nov 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115570588919411719</guid>
                    <title>Umbraco Spark Diversity programme launches</title>
                    <link>https://umbracocommunity.social/@umbracospark/115570588919411719</link>
                    <description><![CDATA[&#x1F389; Have you heard about the Umbraco Spark Diversity programme? We&#x27;re offering FREE tickets to ensure everyone can attend the conference, regardless of background, gender, race, or experience. &#x1F449; Apply or nominate today: https://shorturl.at/YHwkj for a chance to receive a free ticket. Let&#x27;s build a tech community where everyone is included. &#x1F31F;  #umbracospark #diversityintech #umbraco]]></description>
                    <pubDate>Tue, 18 Nov 2025 12:19:08 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@UmbHost/115564453942437414</guid>
                    <title>.NET 10 is now available across all of UmbHost shared hosting</title>
                    <link>https://umbracocommunity.social/@UmbHost/115564453942437414</link>
                    <description><![CDATA[.NET 10 is now available across all of our shared hosting!This means you can now deploy Umbraco 17 and other dotnet 10 applications to us!Get started here &#x1F449; https://umbhost.net/hosting/cloud-umbraco-hosting #hosting #umbraco #dotnet #dotnet10 #sharedhosting]]></description>
                    <pubDate>Mon, 17 Nov 2025 10:17:17 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/don-t-outsource-your-ai-strategy/</guid>
                    <title>Don&#x2019;t outsource your AI strategy</title>
                    <link>https://umbraco.com/blog/don-t-outsource-your-ai-strategy/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 17 Nov 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115553246336530772</guid>
                    <title>The first Umbraco Education Program in India is a Huge Success</title>
                    <link>https://umbracocommunity.social/@umbraco/115553246336530772</link>
                    <description><![CDATA[Two weeks ago, the first Umbraco Education Program in India took place &#x1F1EE;&#x1F1F3; Over 50 LJ University students spent two days learning and earned their first Umbraco Certification &#x1F393;&#x1F44F; Thanks to all involved, especially Nikhil Prajapati and Giriraj Digital &#x1F64C;#Umbraco]]></description>
                    <pubDate>Sat, 15 Nov 2025 14:40:38 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115547942825928208</guid>
                    <title>Sign up to the Umbraco Winter Keynote 2026</title>
                    <link>https://umbracocommunity.social/@umbraco/115547942825928208</link>
                    <description><![CDATA[Imagine it&#x2019;s February 3rd, 4:30 PM CET. Your team is gathered in your favorite meeting room, drinks in hand, screens glowing&#x2026; all eyes on the Umbraco Winter Keynote 2026 &#x2744;&#xFE0F;&#x1F525;You guessed it! You can now sign up for the Winter Keynote and grab a front-row view of how Umbraco will empower you to win in 2026 - all free and online.&#x1F680; Sign up now (and we&#x2019;ll send the recording if you can&#x2019;t join live).https://umbra.co/3WVEPCd #Umbraco]]></description>
                    <pubDate>Fri, 14 Nov 2025 12:49:35 Z</pubDate>
                        <category>hq</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.oembed.indiveo</guid>
                    <title>Umbraco.Community.OEmbed.Indiveo</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.oembed.indiveo</link>
                    <description><![CDATA[A lightweight Umbraco CMS plugin that adds OEmbed support for Indiveo directly into the Umbraco Rich Text Editor (RTE)]]></description>
                    <pubDate>Fri, 14 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/shorts/VYvhbpQpCXY</guid>
                    <title>ClerksWell Charity Hackathon Call for Attendees</title>
                    <link>https://www.youtube.com/shorts/VYvhbpQpCXY</link>
                    <description><![CDATA[The ClerksWell Charity Hackathon is happening on 28th November in London &#xA;&#xA;If you can get to London on that day, please sign up to our free event&#xA;&#xA;https://clerkswell.com/hackathon25&#xA;&#xA;FAQs&#xA; &#xA;&#x1F193; Is it free to enter?&#xA;Yes the hackathon is completely free to enter, you just need to make your way to London for the day. Food and drink will also be provided. If you live outside of London and travel costs are preventing you from attending please get in touch as we may be able to help you.&#xA; &#xA;&#x1F3C6; What are the prizes?&#xA;There will be two winning teams, one for each of the two charity challenges.&#xA;Each team will consist of 2 people.&#xA;The prize for each winning team is:&#xA;1 Codegarden ticket&#xA;1 Expert Training &amp; Certification bundle&#xA;If your team wins, you&#x2019;ll need to decide between yourselves who gets which prize.&#xA; &#xA;&#x1F4BB; How much Umbraco do we need to use?&#xA;There are no strict limits, just make sure you use Umbraco 17 in some way. It doesn&#x2019;t need to be a full-blown implementation. Be creative!&#xA; &#xA;&#x23F1; What if we can&#x27;t finish a working solution in time?&#xA;That&#x2019;s totally fine. The judges will assess entries based on several criteria, including usability and how well the idea meets the brief. Even if your solution isn&#x2019;t fully working, your concept and approach still count.&#xA; &#xA;&#x1F465; Do I need to already have a teammate?&#xA;No. We&#x2019;ve shared a private Slack channel with attendees where you can find a partner and form a team.&#xA; &#xA;&#x1F9E0; How good do I need to be at coding?&#xA;You don&#x2019;t need to be an expert. If you team up with someone whose skills complement yours, you&#x2019;ll be in a great position. For example, if you&#x27;re stronger in front-end or UX, you could pair with someone who&#x2019;s more backend-focused.&#xA;There will also be people around to help if you get stuck.&#xA; &#xA;&#x1F552; What are the timings? What if I arrive late or leave early?&#xA;Coding starts at 10:00 and ends at 16:00.&#xA;If travel is a challenge, it&#x2019;s okay to arrive a bit late or leave slightly early. Just make sure you&#x2019;ve coordinated with your teammate in advance so you&#x2019;re aligned on what you&#x2019;ll be working on.&#xA; &#xA;&#x1F310; Can I attend online?&#xA;This is an in-person event only.&#xA;However, the challenge briefs are public, and you&#x2019;re welcome to try them out on your own. If you&#x2019;d like to share your work with the charities afterwards, we&#x2019;d be happy to connect you.&#xA; &#xA;&#x1FAF1; Can I take part if I already know the charities or ClerksWell?&#xA;Yes, that&#x2019;s not a problem. There are six independent judges, and ClerksWell won&#x2019;t be involved in judging, to keep things fair.&#xA; &#xA;&#x2764;&#xFE0F; Are the charities connected to ClerksWell?&#xA;The charities are not clients of ClerksWell, but they are close to our hearts:&#xA;Headway was suggested by Paul, who previously worked at an agency that built their website. Several of Paul&#x2019;s family members have experienced brain injuries and benefited from Headway&#x2019;s services.&#xA;Restore Hope was suggested by Marc from ClerksWell, who has volunteered with them and admires the work they do in his local area.&#xA;&#xA;Let me know if you have any others to add or want to change any. We are checking with Lotte about the one which mentions help with travel, so please don&#x27;t publish yet.]]></description>
                    <pubDate>Thu, 13 Nov 2025 09:53:52 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115536732286824249</guid>
                    <title>Join your nearest Umbraco 17 Launch Party</title>
                    <link>https://umbracocommunity.social/@umbraco/115536732286824249</link>
                    <description><![CDATA[Communities worldwide are celebrating the Umbraco 17 release this November &#x1F389; Whether you&#x2019;re a long-time contributor or new to the community, this is your chance to celebrate the latest release and connect with fellow Umbraco enthusiasts around the world &#x1F30D;Join the party nearest to you:Sydney, Nov 27: https://umbra.co/4r96Sft Amersfoort, Nov 27: https://umbra.co/4qPPa0fLondon, Nov 28: https://umbra.co/4nMU0sjGlasgow, Dec 4: https://umbra.co/49G4mXy #Umbraco]]></description>
                    <pubDate>Thu, 13 Nov 2025 08:55:13 Z</pubDate>
                        <category>hq</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115541171300478078</guid>
                    <title>Umbraco CMS 17.0.0 Release Candidate 2 is out</title>
                    <link>https://umbracocommunity.social/@umbraco/115541171300478078</link>
                    <description><![CDATA[Umbraco CMS 17.0.0 Release Candidate 2 is out &#x1F389; Release notes here&#x1F447;  https://releases.umbraco.com/release/umbraco/Umbraco-CMS/17.0.0-rc2 https://github.com/umbraco/Umbraco-CMS/releases/tag/release-17.0.0-rc2 Umbraco CMS 16.4.0 Release Candidate is out &#x1F389; Release notes here &#x1F447; https://releases.umbraco.com/release/umbraco/Umbraco-CMS/16.4.0 https://github.com/umbraco/Umbraco-CMS/releases/tag/release-16.4.0-rc #Umbraco]]></description>
                    <pubDate>Thu, 13 Nov 2025 08:55:00 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@abjerner/115533190585231364</guid>
                    <title>A pre-release of Skybrud Redirects is now available for Umbraco 17</title>
                    <link>https://umbracocommunity.social/@abjerner/115533190585231364</link>
                    <description><![CDATA[A pre-release of Skybrud Redirects is now available for #Umbraco 17 &#x1F389;https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0-beta001]]></description>
                    <pubDate>Wed, 12 Nov 2025 11:36:16 Z</pubDate>
                        <category>social</category>
                        <category>mastodon</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0-beta001</guid>
                    <title>Skybrud.Umbraco.Redirects 17.0.0-beta001</title>
                    <link>https://github.com/skybrud/Skybrud.Umbraco.Redirects/releases/tag/v17.0.0-beta001</link>
                    <description><![CDATA[Award winning redirects manager for Umbraco.]]></description>
                    <pubDate>Tue, 11 Nov 2025 21:17:40 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://gibe.digital/blog/umbraco-spark-2026/</guid>
                    <title>Get Ready For Umbraco Spark 2026</title>
                    <link>https://gibe.digital/blog/umbraco-spark-2026/</link>
                    <description><![CDATA[https://gibe.digital/blog/umbraco-spark-2026/]]></description>
                    <pubDate>Tue, 11 Nov 2025 18:21:50 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115513196904042264</guid>
                    <title>Join Lotte and Emma at .NET Conf 2025</title>
                    <link>https://umbracocommunity.social/@umbraco/115513196904042264</link>
                    <description><![CDATA[We&#x2019;re excited to speak at .NET Conf 2025! &#x1F389;Join Lotte and Emma for &#x201C;From Frameworks to Friendliness,&#x201D; a session on building more welcoming open source communities. Catch it this Thursday at https://umbra.co/3Jnqzz7 &#x1F499;#Umbraco]]></description>
                    <pubDate>Tue, 11 Nov 2025 12:57:53 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115525292757531612</guid>
                    <title>Early bird tickets for Umbraco Spark 2026 are out now!</title>
                    <link>https://umbracocommunity.social/@umbracospark/115525292757531612</link>
                    <description><![CDATA[&#x1F39F;&#xFE0F; Early bird tickets for Umbraco Spark 2026 are out now!Join us on 20 March in Bristol for a day of Umbraco insights, community collaboration, and open-source energy.Grab your ticket while early bird pricing lasts &#x2192; https://www.tickettailor.com/events/umbukfdn/1896581 #Umbraco #UmbracoSpark #Innovation]]></description>
                    <pubDate>Mon, 10 Nov 2025 12:01:38 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.spreaker.com/episode/live-from-umbraco-in-the-city-manchester--68487985</guid>
                    <title>Live from Umbraco in the City Manchester!</title>
                    <link>https://www.spreaker.com/episode/live-from-umbraco-in-the-city-manchester--68487985</link>
                    <description><![CDATA[All four of us were together in-person - for the first time over two years - at Umbraco in the City, a community-organised event in Manchester. We talked about what contributions look like to a mature open-source product with a team of full time engineers working on the codebase. Are code contributions still the &#x201C;gold-standard&#x201D;, were they ever? We discuss this and what other types of contrubitions make a meaningful difference to the Umbraco CMS in front of a wonderful live audience.&lt;br /&gt;Thanks to our guests - Michael Latouche, Paul Seal and our new best friend Kieron McIntyre - for taking the microphone and sharing their contribution stories.&lt;br /&gt;High five to them and the audience, we had a lot of fun!&#xA0;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Links&lt;/b&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.umbracointhe.city/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Umbraco in the City&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://community.umbraco.com/learn-about-the-community/community-teams/the-core-collaborators/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Core collabs team page&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://our.umbraco.com/packages/starter-kits/clean-starter-kit/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Paul Seal&#x2019;s starter kit&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/posts/paul-seal-3064892_umbraco-activity-7386779278002003969-SoU-?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAAW74_YBV3u9EQnwiLbNY-AGuarbE2fo6O0&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;post about the PR from Andy&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://umb.fyi/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;UMB.FYI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://umbracospark.com/&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Umbraco Spark&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;]]></description>
                    <pubDate>Mon, 10 Nov 2025 07:00:09 Z</pubDate>
                        <category>community</category>
                        <category>podcast</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/algolia-provider-for-umbraco-search/</guid>
                    <title>Algolia provider for Umbraco Search</title>
                    <link>https://kjac.dev/posts/algolia-provider-for-umbraco-search/</link>
                    <description><![CDATA[An introduction to the Algolia provider for Umbraco Search, including a discussion about best practices for Umbraco data in Algolia.]]></description>
                    <pubDate>Mon, 10 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://mastodon.social/@deanleigh/115521085438233411</guid>
                    <title>Help Dean Leigh race money for Bowel Cancer UK this Christmas</title>
                    <link>https://mastodon.social/@deanleigh/115521085438233411</link>
                    <description><![CDATA[I know were still in Movember but I need an early start to get a decent santa beard!Please help me raise some money for @bowelcanceruk  #Decembeard no matter how small it all helps.https://www.justgiving.com/page/dean-leigh-decembeard-2025]]></description>
                    <pubDate>Sun, 09 Nov 2025 20:56:30 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/adage.engage.inspector</guid>
                    <title>Adage.Engage.Inspector</title>
                    <link>https://marketplace.umbraco.com/package/adage.engage.inspector</link>
                    <description><![CDATA[Visual A/B test and personalization inspector for Umbraco Engage. Enables content editors to visually select and configure content variants directly in the frontend.]]></description>
                    <pubDate>Sat, 08 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owainjones.dev/blog/umbracokalaset-2025/</guid>
                    <title>Umbracokalaset 2025 - Swedish Umbraco Festival</title>
                    <link>https://owainjones.dev/blog/umbracokalaset-2025/</link>
                    <description><![CDATA[I had the absolute pleasure of speaking at Umbracokalaset 2025 in Stockholm! From friendly faces and great conversations to an incredible response to my talk on building better content editor experiences, it was an unforgettable week with the amazing Umbraco community.]]></description>
                    <pubDate>Fri, 07 Nov 2025 12:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://85c1b210-886e-4021-8488-e36778c85276.azurewebsites.net/blog/umbracokalaset-2025/</guid>
                    <title>Umbracokalaset 2025 - Swedish Umbraco Festival</title>
                    <link>https://85c1b210-886e-4021-8488-e36778c85276.azurewebsites.net/blog/umbracokalaset-2025/</link>
                    <description><![CDATA[I had the absolute pleasure of speaking at Umbracokalaset 2025 in Stockholm! From friendly faces and great conversations to an incredible response to my talk on building better content editor experiences, it was an unforgettable week with the amazing Umbraco community.]]></description>
                    <pubDate>Fri, 07 Nov 2025 12:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/nanokernel/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-1lj9</guid>
                    <title>Nah, the Umbraco Community Hasn&#x27;t Lost It&#x27;s Spark - We Have</title>
                    <link>https://dev.to/nanokernel/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-1lj9</link>
                    <description><![CDATA[&lt;p&gt;What a contradictory title, please let me explain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This article is a response to Owain William&#x27;s thought-provoking blogpost, &lt;a href=&quot;https://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/&quot; rel=&quot;noopener noreferrer&quot;&gt;Has the Umbraco Community lost its Spark?&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;While I understand where Owain is coming from and I too feel the change, I&#x27;d like to set up a defense: It&#x27;s not Umbraco, it&#x27;s not you, it&#x27;s not me.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So what &lt;em&gt;has&lt;/em&gt; changed?&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  We&#x27;re Exhausted&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re all grinding harder just to tread water, trying our best to cling to the standard of living we once took for granted. Less time for Discord banter, fewer swag selfies on Umbraco Tees Day. Sound familiar?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;While I agree with Owain, I just can&#x27;t stand this characterization assassination of the Umbraco community. This despair (dramatic?) is all over the place. I have seen video after video show up on my YouTube feed lamenting the zombification and decline of community over the last few years.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In one, the host described a recent trip to the Kentucky Derby where he was shocked at the low attendance, the lack of inebriation of those who did attend, and at the relatively disconnected feeling of the entire event - a sharp contrast against the years of decades of the past.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I have never been, but I feel it too. Christmases, thanksgivings, halloweens, independence days, none of these feel the same lately. Hell, malls barely bother with holiday displays anymore, and it doesn&#x27;t seem to matter because no one has the time to enjoy them anyway. These occasions now blend into any other day, and I feel horrible for this generation of children being deprived of the same joy I had.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;People are so eager to blame this zombification on smartphone usage, but that&#x27;s such a lazy analysis and so far off the mark that it makes my skin crawl.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this response, I&#x27;ll walk you through how we are being slowly and invisible suffocated by compounding financialization of every segment of our economy. It&#x27;s crept in over decades, draining joy from what we love, bit by bit.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You probably haven&#x27;t heard this story before. I&#x27;ll admit up front: this is going to be contrarian and politically incorrect, but I promise to do my best to be truthful and objective.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  How Did We Get Here?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The 2008 crisis wasn&#x27;t fixed; it was papered over. Politicians bailed out &lt;a href=&quot;https://www.youtube.com/watch?v=A25EUhZGBws&quot; rel=&quot;noopener noreferrer&quot;&gt;arrogant and irresponsible bankers&lt;/a&gt; in a now familiar pattern of &quot;socialism for corporations, capitalism for the rest.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &quot;fix&quot; for the 2008 crisis was to pretend the whole thing never happened: reinflate the asset bubbles, but with stricter rules this time. This kicked off the funny-money era, but bad policy started in 1971 when we ditched the gold standard, a Bretton Woods/post-WWII holdover, to cover the costs of the Vietnam War.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Through the 2010s, every hiccup or slowdown triggered Fed rate cuts, plunging deeper into a ZIRP (zero interest rate policy) environment. We had historic low interest rates on everything, and money was cheap. Each rate cut did in fact stimulate economic activity and kept the economy from actually, finally, correcting. You likely observed this in your own life with residential home mortgages, or in the news with historic investment banking deals.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Analysts gawked at the &quot;depravity&quot; of this tactic, which was a decisive turn from what is considered conventional and sound financial policy. Trillions of dollars were printed out of thin air, also know as QE (quantitative easing), which debased the US dollar. These analysts were predicting bubble-pop events for years, none of which materialized, always stymied by another round of QE.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Still, the dollar&#x27;s status was protected, as it held as reserve king in petrodollar trades (buying and selling oil), and foreign Treasury hoarding (foreign countries buying our debt). Or to put it another way, by being &quot;the least dirtiest toilet paper&quot; around.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It worked, until September 2019&#x27;s repo crisis. Liquidity vanished, rates spiked to 10%. Banks swapped assets (consumer mortgages, consumer auto loans) for Fed cash to avoid collapse. The trigger still isn&#x27;t clear, but most believe it to be a combination of regulatory scars, reserve shortages, and second-order pain from Trump&#x27;s 2018 tariffs, which sparked a mini-recession in manufacturing. By September 2019, the runway for QE had run out. Interest rates couldn&#x27;t be cut any further, and the roosters were coming home to roost.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This should have triggered a crash, or &quot;correction,&quot; but just months later the world was thrown into COVID chaos, while spending bills were quickly passed to inject liquidity into the financial system and stimulate the economy, in the range of trillions of dollars. This effectively masked the repo crisis, but kicked off insidious inflation, which has transformed into &quot;stagflation.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In a &quot;stagflation&quot; scenario, prices remain high, even as demand drops. This irrational, but very real phenomenon runs contrary to Econ 101, which tells us that as the cost of goods rise, demand drops (and vice versa). There&#x27;s no real way to handle this scenario, as a citizen, except to hold hard assets (real estate, gold), invest (and hope the market doesn&#x27;t pop), or find ways to hustle harder and make more money. Financial institutions don&#x27;t really have an answer for addressing stagflation, either, except to play shell games increasing and decreasing interest rates in a desperate attempt to try and keep the circus going.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Just last week (October 2025), the repo market way haywire again as banks struggle with liquidity issues, speculated to be caused by the current longest-ever historical US government shutdown.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There are two ways to solve this stagflation problem, and politicians are now facing a cliff: they can decide to keep printing and let inflation rip, or decide to let a very painful but proper correction hit (overdue since 2008), with the upside that we return to value and fundamental financial policy. Take your best on which they&#x27;ll pick.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  It&#x27;s Not Umbraco&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re all now working harder to maintain the standard of living we&#x27;re used to, we have less time and means to participate in communities. Our employers tighten down in austerity, layoffs continue to accelerate across the industry, creating a chilling effect.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;And then one day, all of the sudden, you&#x27;re just tired. Real, fucking, tired.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Kudos to Owain for spotting this, but it&#x27;s not just the Umbraco community. The broader financialization of our economy means this broken system applies to every facet of your life, even your &lt;a href=&quot;https://x.com/_Investinq/status/1983972008301097160&quot; rel=&quot;noopener noreferrer&quot;&gt;Chipotle burritos&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This isn&#x27;t a failure of the Umbraco community not showing up, or of Umbraco not investing enough, or of the devrel team not working hard enough (how many festivals and community meetups have been re-ignited in the last few years?). I&#x27;d actually argue that Umbraco and festival committees seem to be doing more than I can ever remember. What we&#x27;re all feeling is the endgame of an ugly economic war so raw that &lt;a href=&quot;https://x.com/SecScottBessent/status/1985799963201421646&quot; rel=&quot;noopener noreferrer&quot;&gt;no politician would dare be honest about&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Inflation is silently stealing our live, breaking our spirits, and it&#x27;s extremely unfair.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 06 Nov 2025 22:45:56 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/nanokernel/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-8n</guid>
                    <title>Nah, the Umbraco Community Hasn&#x27;t Lost It&#x27;s Spark - We Have</title>
                    <link>https://dev.to/nanokernel/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-8n</link>
                    <description><![CDATA[&lt;p&gt;What a contradictory title, please let me explain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This article is a response to Owain William&#x27;s thought-provoking blogpost, &lt;a href=&quot;https://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/&quot; rel=&quot;noopener noreferrer&quot;&gt;Has the Umbraco Community lost its Spark?&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;While I understand where Owain is coming from and I too feel the change, I&#x27;d like to set up a defense: It&#x27;s not Umbraco, it&#x27;s not you, it&#x27;s not me.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So what &lt;em&gt;has&lt;/em&gt; changed?&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  We&#x27;re Exhausted&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re all grinding harder just to tread water, trying our best to cling to the standard of living we once took for granted. Less time for Discord banter, fewer swag selfies on Umbraco Tees Day. Sound familiar?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;While I agree with Owain, I just can&#x27;t stand this characterization assassination of the Umbraco community. This despair (dramatic?) is all over the place. I have seen video after video show up on my YouTube feed lamenting the zombification and decline of community over the last few years.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In one, the host described a recent trip to the Kentucky Derby where he was shocked at the low attendance, the lack of inebriation of those who did attend, and at the relatively disconnected feeling of the entire event - a sharp contrast against the years of decades of the past.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I have never been, but I feel it too. Christmases, thanksgivings, halloweens, independence days, none of these feel the same lately. Hell, malls barely bother with holiday displays anymore, and it doesn&#x27;t seem to matter because no one has the time to enjoy them anyway. These occasions now blend into any other day, and I feel horrible for this generation of children being deprived of the same joy I had.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;People are so eager to blame this zombification on smartphone usage, but that&#x27;s such a lazy analysis and so far off the mark that it makes my skin crawl.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this response, I&#x27;ll walk you through how we are being slowly and invisible suffocated by compounding financialization of every segment of our economy. It&#x27;s crept in over decades, draining joy from what we love, bit by bit.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You probably haven&#x27;t heard this story before. I&#x27;ll admit up front: this is going to be contrarian and politically incorrect, but I promise to do my best to be truthful and objective.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  How Did We Get Here?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The 2008 crisis wasn&#x27;t fixed; it was papered over. Politicians bailed out &lt;a href=&quot;https://www.youtube.com/watch?v=A25EUhZGBws&quot; rel=&quot;noopener noreferrer&quot;&gt;arrogant and irresponsible bankers&lt;/a&gt; in a now familiar pattern of &quot;socialism for corporations, capitalism for the rest.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The &quot;fix&quot; for the 2008 crisis was to pretend the whole thing never happened: reinflate the asset bubbles, but with stricter rules this time. This kicked off the funny-money era, but bad policy started in 1971 when we ditched the gold standard, a Bretton Woods/post-WWII holdover, to cover the costs of the Vietnam War.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Through the 2010s, every hiccup or slowdown triggered Fed rate cuts, plunging deeper into a ZIRP (zero interest rate policy) environment. We had historic low interest rates on everything, and money was cheap. Each rate cut did in fact stimulate economic activity and kept the economy from actually, &lt;em&gt;finally,&lt;/em&gt; correcting. You likely observed this in your own life with residential home mortgages, or in the news with historic investment banking deals.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Analysts gawked at the &quot;depravity&quot; of this tactic, which was a decisive turn from what is considered conventional and sound financial policy. Trillions of dollars were printed out of thin air, also know as QE (quantitative easing), which debased the US dollar. These analysts were predicting bubble-pop events for years, none of which materialized, always stymied by another round of QE.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Still, the dollar&#x27;s status was protected, as it held as reserve king in petrodollar trades (buying and selling oil), and foreign Treasury hoarding (foreign countries buying our debt). Or to put it another way, by being &quot;the least dirtiest toilet paper&quot; around.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It worked, until September 2019&#x27;s repo crisis. Liquidity vanished, rates spiked to 10%. Banks swapped assets (consumer mortgages, consumer auto loans) for Fed cash to avoid collapse. The trigger still isn&#x27;t clear, but most believe it to be a combination of regulatory scars, reserve shortages, and second-order pain from Trump&#x27;s 2018 tariffs, which sparked a mini-recession in manufacturing. By September 2019, the runway for more QE had run out. Interest rates couldn&#x27;t be cut any further, and the roosters were coming home to roost.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This should have triggered a crash, or &quot;correction,&quot; but just months later the world was thrown into COVID chaos, while spending bills were quickly passed to inject liquidity into the financial system and stimulate the economy, in the range of trillions of dollars. This effectively masked the repo crisis, but kicked off insidious inflation, which has transformed into &quot;stagflation.&quot;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In a &quot;stagflation&quot; scenario, prices remain high, even as demand drops. This irrational, but very real phenomenon runs contrary to Econ 101, which tells us that as the cost of goods rise, demand drops (and vice versa). There&#x27;s no real way to handle this scenario, as a citizen, except to hold hard assets (real estate, gold), invest (and hope the market doesn&#x27;t pop), or find ways to hustle harder and make more money. Financial institutions don&#x27;t really have an answer for addressing stagflation, either, except to play shell games increasing and decreasing interest rates in a desperate attempt to try and keep the circus going.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Just last week (October 2025), the repo market way haywire &lt;em&gt;again&lt;/em&gt; as banks struggle with liquidity issues, speculated to be caused by the current longest-ever historical US government shutdown.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There are two ways to solve this stagflation problem, and politicians are now facing a cliff: they can decide to keep printing and let inflation rip, or decide to let a very painful but proper correction hit (overdue since 2008), with the upside that we return to value and fundamental financial policy. Take your best on which they&#x27;ll pick.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  It&#x27;s Not Umbraco&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We&#x27;re all now working harder to maintain the standard of living we&#x27;re used to, we have less time and means to participate in communities. Our employers tighten down in austerity, layoffs continue to accelerate across the industry, creating a chilling effect.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;And then one day, all of the sudden, you&#x27;re just tired. Real, fucking, tired.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Kudos to Owain for spotting this, but it&#x27;s not just the Umbraco community. The broader financialization of our economy means this broken system applies to every facet of your life, even your &lt;a href=&quot;https://x.com/_Investinq/status/1983972008301097160&quot; rel=&quot;noopener noreferrer&quot;&gt;Chipotle burritos&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This isn&#x27;t a failure of the Umbraco community not showing up, or of Umbraco not investing enough, or of the devrel team not working hard enough (how many festivals and community meetups have been re-ignited in the last few years?). I&#x27;d actually argue that Umbraco and festival committees seem to be doing more than I can ever remember. What we&#x27;re all feeling is the endgame of an ugly economic war so raw that &lt;a href=&quot;https://x.com/SecScottBessent/status/1985799963201421646&quot; rel=&quot;noopener noreferrer&quot;&gt;no politician would dare be honest about&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Inflation is silently stealing our live, breaking our spirits, and it&#x27;s extremely unfair.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 06 Nov 2025 22:30:55 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/eg-regex/</guid>
                    <title>Regularly Expressed Regular Expressions</title>
                    <link>https://joe.gl/ombek/blog/eg-regex/</link>
                    <description><![CDATA[&lt;p&gt;Examples of common or useful regular expressions I&#x27;ve created over the years.&lt;/p&gt;&#xA;&lt;h2&gt;Useful expressions&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I&#x27;ll keep coming back to this blog post as I create more, so it might be worth a bookmark!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The regular expressions that start with &lt;code&gt;^&lt;/code&gt; (start of string/line) and end with &lt;code&gt;$&lt;/code&gt; (end of string/line) will ensure the whole string matches. This is useful for validating a string is in the format you expect.&#xA;To find a string in the format you expect inside another string, these can be omitted. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;Most of these regular expressions can be changed to the other method simply by adding/removing the &lt;code&gt;^&lt;/code&gt;(start of string/line) and &lt;code&gt;$&lt;/code&gt; (end of string/line) characters at the start and end respectively.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;GUID&lt;/h3&gt;&#xA;&#xA;&lt;h4&gt;Validate a string is a GUID&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;^(?:\((?=.*\))|{(?=.*}))?[a-fA-F0-9]{8}(?:-(?=(?:.*-){3}))?(?:[a-fA-F0-9]{4}-?){3}[a-fA-F0-9]{12}(?:\)(?&amp;lt;=\(.*)|}(?&amp;lt;={.*))?$&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://regexr.com/8hv1f&quot;&gt;Open in RegExr&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Matches guids in most formats that .NET&#x27;s &lt;code&gt;Guid.Parse&lt;/code&gt; can handle (except &lt;code&gt;0x&lt;/code&gt; format), is case insensitive, dashes and brackets are optional, but brackets must match.&lt;/p&gt;&#xA;&#xA;&lt;h4&gt;Replace a GUID inside a longer string&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;As above without leading &lt;code&gt;^&lt;/code&gt; and trailing &lt;code&gt;$&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h4&gt;Find inside a longer string&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;[a-fA-F0-9]{8}(?:-(?=(?:.*-){3}))?(?:[a-fA-F0-9]{4}-?){3}[a-fA-F0-9]{12}&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://regexr.com/8hv1l&quot;&gt;Open in RegExr&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This one is simpler as it doesn&#x27;t need to worry about the brackets - we&#x27;re looking for the bare minimum that constitutes a GUID.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;YouTube URLs&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;https?:\/\/(?:www.)?(?:(?:youtube.com\/(?:v\/|embed\/|watch\?v=|\&amp;amp;v=))|(?:youtu.be\/))([^#\&amp;amp;\?\s]&#x2B;)\S*&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://regexr.com/8hup7&quot;&gt;Open in RegExr&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Will match many forms of YouTube URL (default, short links, embed). The first group in the match will be the video ID which can be used to construct an embed URL.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Vimeo URLs&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;https?:\/\/(?:(?:www\.)|(?:player\.))?(?:vimeo\.com\/(?:channels\/(?:\w&#x2B;\/)?|groups\/(?:\w&#x2B;\/)?videos\/|album\/(?:\d&#x2B;\/)?video\/|video\/|external\/)?(\d&#x2B;))\S*&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://regexr.com/8hupd&quot;&gt;Open in RegExr&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With thanks to &lt;a href=&quot;https://huskey.uk/&quot;&gt;Mike Masey&lt;/a&gt; for the starting point of this one.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 06 Nov 2025 01:42:38 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/carole-rennie-logan-13227328_umbraco-scotland-linkedin-activity-7391889380203966464-5faA?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</guid>
                    <title>Announcing Umbraco Scotland!</title>
                    <link>https://www.linkedin.com/posts/carole-rennie-logan-13227328_umbraco-scotland-linkedin-activity-7391889380203966464-5faA?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</link>
                    <description><![CDATA[https://www.linkedin.com/posts/carole-rennie-logan-13227328_umbraco-scotland-linkedin-activity-7391889380203966464-5faA?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw]]></description>
                    <pubDate>Wed, 05 Nov 2025 18:36:13 Z</pubDate>
                        <category>community</category>
                        <category>linkedin</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://forem.com/luukpeters/no-you-dont-need-lit-vite-or-typescript-to-extend-the-umbraco-backoffice-2mg6</guid>
                    <title>No, you don&#x2019;t need Lit, Vite, or TypeScript to Extend the Umbraco Backoffice</title>
                    <link>https://forem.com/luukpeters/no-you-dont-need-lit-vite-or-typescript-to-extend-the-umbraco-backoffice-2mg6</link>
                    <description><![CDATA[&lt;p&gt;One of the biggest misconceptions I see pop up regularly among developers who start working on the new Umbraco &quot;Bellissima&quot; backoffice for the first time is that they think you need a lot of scaffolding and an entire build pipeline to extend the Umbraco 14&#x2B; backoffice.  &lt;/p&gt;&#xA;&#xA;&lt;p&gt;This makes it seem like arbitrary extensions to the backoffice are very hard, especially for backend developers who aren&#x2019;t into npm and such.&lt;br&gt;&lt;br&gt;&#xA;I&#x27;m here to tell you that you don&#x2019;t need to use Lit, Vite, or TypeScript if you don&#x2019;t want to.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why do so many developers think you need all that scaffolding?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For the most part, this is a documentation thing. Umbraco itself uses Lit, Vite, and TypeScript in its codebase, and as a result, most examples in the documentation are based on Lit. Also, the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/development-flow&quot; rel=&quot;noopener noreferrer&quot;&gt;Setup your development environment&lt;/a&gt; article talks about setting up TypeScript and Vite, which reinforces that impression.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You might think it&#x2019;s just a matter of better documentation, and although I agree that the docs could improve, the problem is actually a bit bigger.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A well-designed (and overly flexible) architecture&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The funny thing is that biggest problem is that the backoffice architecture is really well designed. That sounds like a contradiction, but hear me out.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The backoffice is built to be flexible, extensible, and framework-agnostic. Because of this, there are actually very few hard requirements for extending it.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UI extensions (like property editors, buttons, or workspaces) must be web components.&lt;/li&gt;&#xA;&lt;li&gt;Other extensions (like repositories or stores) must follow the requirements of their extension type.&lt;/li&gt;&#xA;&lt;li&gt;And lastly, you need an &lt;code&gt;umbraco-package.json&lt;/code&gt; file to register the extensions.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But that&#x2019;s practically it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This means that as long as you follow these basic rules, you can create your extension however you want. Yes, Umbraco uses TypeScript for their code, Lit for its web components, and Vite to bundle it all, but you don&#x2019;t have to. You can use your own framework and bundler if you like, as long as your bundles use the ES module syntax.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  No scaffolding? No problem&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You can also use plain JavaScript and skip any compilation or bundling entirely. As long as the requirements are met. That means no npm packages, no build scripts, and no scaffolding.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, there are many ways to go about this. And that&#x2019;s what I mean when I say that the backoffice architecture is both a blessing and a curse: there are so many possibilities that it&#x2019;s impossible to document every one of them.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That said, I also believe the documentation could do a better job at showing this flexibility. I&#x2019;m part of the Umbraco Documentation Community Team, and I try to spread the message of this blog wherever I can.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  When to use what&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There&#x2019;s no absolute right or wrong, but here&#x2019;s my opinion:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Use plain JavaScript for small and simple things, like a &lt;a href=&quot;https://forum.umbraco.com/t/custom-block-grid-views-too-complicated/2894/49?u=luuk&quot; rel=&quot;noopener noreferrer&quot;&gt;block view&lt;/a&gt; or a small Tiptap extension.&lt;/li&gt;&#xA;&lt;li&gt;For anything bigger, it&#x2019;s worth investing the time to use TypeScript &#x2014; it elevates the quality of your code and prevents bugs.&#xA;Especially if you&#x2019;re a C# developer, you&#x2019;ll probably appreciate the strong typing.&lt;/li&gt;&#xA;&lt;li&gt;Once you&#x2019;re already using npm and build scripts, it&#x2019;s just a small step to introduce Lit and bundle it with Vite.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;In the end, it&#x2019;s not that hard. I&#x2019;m primarily a C# backend developer, and yes, the learning curve exists, but I can honestly say that the quality of my packages has improved significantly since adopting these tools.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I&#x2019;d also recommend going with Lit for elements. The Umbraco source code and most examples use it, so unless you have a specific reason not to, it&#x2019;s a good default.&lt;br&gt;&lt;br&gt;&#xA;Lit is lightweight and removes the most annoying parts of writing web components by hand.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Want to learn more?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Because I don&#x2019;t want to bloat this blog with tons of examples, I&#x2019;ll wrap up with a few useful links if you want to dive deeper:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I recently rewrote &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-registry&quot; rel=&quot;noopener noreferrer&quot;&gt;the documentation on the extension registry&lt;/a&gt;, which hopefully makes things a bit clearer.&#xA;&lt;/li&gt;&#xA;&lt;li&gt;For the Umbraco Autumn Contribution Challenge 2025, I created an example showing how to register a custom condition to an existing extension.&#xA;The example illustrates three different approaches (with the same result):&#xA;vanilla JavaScript, TypeScript without bundling, and Lit &#x2B; Vite.&#xA;You can find it on &lt;a href=&quot;https://github.com/Luuk1983/UmbracoExamples/blob/main/ConditionsToExistingExtensions/README.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;In short: you don&#x2019;t need all the scaffolding to extend Umbraco&#x2019;s backoffice, but knowing how to use it can make your life a lot easier.&lt;/p&gt;]]></description>
                    <pubDate>Wed, 05 Nov 2025 10:30:59 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/luukpeters/no-you-dont-need-lit-vite-or-typescript-to-extend-the-umbraco-backoffice-2mg6</guid>
                    <title>No, you don&#x2019;t need Lit, Vite, or TypeScript to Extend the Umbraco Backoffice</title>
                    <link>https://dev.to/luukpeters/no-you-dont-need-lit-vite-or-typescript-to-extend-the-umbraco-backoffice-2mg6</link>
                    <description><![CDATA[&lt;p&gt;One of the biggest misconceptions I see pop up regularly among developers who start working on the new Umbraco &quot;Bellissima&quot; backoffice for the first time is that they think you need a lot of scaffolding and an entire build pipeline to extend the Umbraco 14&#x2B; backoffice.  &lt;/p&gt;&#xA;&#xA;&lt;p&gt;This makes it seem like arbitrary extensions to the backoffice are very hard, especially for backend developers who aren&#x2019;t into npm and such.&lt;br&gt;&lt;br&gt;&#xA;I&#x27;m here to tell you that you don&#x2019;t need to use Lit, Vite, or TypeScript if you don&#x2019;t want to.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why do so many developers think you need all that scaffolding?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For the most part, this is a documentation thing. Umbraco itself uses Lit, Vite, and TypeScript in its codebase, and as a result, most examples in the documentation are based on Lit. Also, the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/development-flow&quot; rel=&quot;noopener noreferrer&quot;&gt;Setup your development environment&lt;/a&gt; article talks about setting up TypeScript and Vite, which reinforces that impression.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You might think it&#x2019;s just a matter of better documentation, and although I agree that the docs could improve, the problem is actually a bit bigger.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  A well-designed (and overly flexible) architecture&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The funny thing is that biggest problem is that the backoffice architecture is really well designed. That sounds like a contradiction, but hear me out.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The backoffice is built to be flexible, extensible, and framework-agnostic. Because of this, there are actually very few hard requirements for extending it.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UI extensions (like property editors, buttons, or workspaces) must be web components.&lt;/li&gt;&#xA;&lt;li&gt;Other extensions (like repositories or stores) must follow the requirements of their extension type.&lt;/li&gt;&#xA;&lt;li&gt;And lastly, you need an &lt;code&gt;umbraco-package.json&lt;/code&gt; file to register the extensions.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;But that&#x2019;s practically it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This means that as long as you follow these basic rules, you can create your extension however you want. Yes, Umbraco uses TypeScript for their code, Lit for its web components, and Vite to bundle it all, but you don&#x2019;t have to. You can use your own framework and bundler if you like, as long as your bundles use the ES module syntax.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  No scaffolding? No problem&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You can also use plain JavaScript and skip any compilation or bundling entirely. As long as the requirements are met. That means no npm packages, no build scripts, and no scaffolding.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, there are many ways to go about this. And that&#x2019;s what I mean when I say that the backoffice architecture is both a blessing and a curse: there are so many possibilities that it&#x2019;s impossible to document every one of them.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That said, I also believe the documentation could do a better job at showing this flexibility. I&#x2019;m part of the Umbraco Documentation Community Team, and I try to spread the message of this blog wherever I can.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  When to use what&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There&#x2019;s no absolute right or wrong, but here&#x2019;s my opinion:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Use plain JavaScript for small and simple things, like a &lt;a href=&quot;https://forum.umbraco.com/t/custom-block-grid-views-too-complicated/2894/49?u=luuk&quot; rel=&quot;noopener noreferrer&quot;&gt;block view&lt;/a&gt; or a small Tiptap extension.&lt;/li&gt;&#xA;&lt;li&gt;For anything bigger, it&#x2019;s worth investing the time to use TypeScript &#x2014; it elevates the quality of your code and prevents bugs.&#xA;Especially if you&#x2019;re a C# developer, you&#x2019;ll probably appreciate the strong typing.&lt;/li&gt;&#xA;&lt;li&gt;Once you&#x2019;re already using npm and build scripts, it&#x2019;s just a small step to introduce Lit and bundle it with Vite.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;In the end, it&#x2019;s not that hard. I&#x2019;m primarily a C# backend developer, and yes, the learning curve exists, but I can honestly say that the quality of my packages has improved significantly since adopting these tools.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I&#x2019;d also recommend going with Lit for elements. The Umbraco source code and most examples use it, so unless you have a specific reason not to, it&#x2019;s a good default.&lt;br&gt;&lt;br&gt;&#xA;Lit is lightweight and removes the most annoying parts of writing web components by hand.&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Want to learn more?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Because I don&#x2019;t want to bloat this blog with tons of examples, I&#x2019;ll wrap up with a few useful links if you want to dive deeper:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I recently rewrote &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-registry&quot; rel=&quot;noopener noreferrer&quot;&gt;the documentation on the extension registry&lt;/a&gt;, which hopefully makes things a bit clearer.&#xA;&lt;/li&gt;&#xA;&lt;li&gt;For the Umbraco Autumn Contribution Challenge 2025, I created an example showing how to register a custom condition to an existing extension.&#xA;The example illustrates three different approaches (with the same result):&#xA;vanilla JavaScript, TypeScript without bundling, and Lit &#x2B; Vite.&#xA;You can find it on &lt;a href=&quot;https://github.com/Luuk1983/UmbracoExamples/blob/main/ConditionsToExistingExtensions/README.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;In short: you don&#x2019;t need all the scaffolding to extend Umbraco&#x2019;s backoffice, but knowing how to use it can make your life a lot easier.&lt;/p&gt;]]></description>
                    <pubDate>Wed, 05 Nov 2025 10:30:59 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.addact.net/blogs/umbraco-website-security-best-practices-2026</guid>
                    <title>Learn to secure your Umbraco website with best practices for 2026</title>
                    <link>https://www.addact.net/blogs/umbraco-website-security-best-practices-2026</link>
                    <description><![CDATA[Learn to secure your Umbraco website with best practices for 2026]]></description>
                    <pubDate>Wed, 05 Nov 2025 09:14:36 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.Skyfish/releases/tag/v13.0.2</guid>
                    <title>Limbo.Umbraco.Skyfish 13.0.2</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.Skyfish/releases/tag/v13.0.2</link>
                    <description><![CDATA[Video picker for inserting Skyfish videos in Umbraco.]]></description>
                    <pubDate>Tue, 04 Nov 2025 13:28:40 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115451691917122141</guid>
                    <title>Volunteers to MC at Umbraco Spark wanted</title>
                    <link>https://umbracocommunity.social/@umbracospark/115451691917122141</link>
                    <description><![CDATA[We&#x2019;re on the lookout for volunteers to MC at Umbraco Spark! &#x1F3A4;&#x1F525; What&#x2019;s in it for you? &#x1F39F;&#xFE0F; FREE ticket to the event &#x1F381; A unique gift! Your duties include: &#x2728; Kicking off each session with enthusiasm &#x23F0; Keeping things on schedule and ensuring everything flows smoothly &#x1F44F; Concluding with thanks and building anticipation for what&#x27;s to come. It&#x27;s simple, enjoyable, and we&#x2019;ll provide a detailed briefing and support calls! &#x1F9E1; Interested? Contact us at umbracospark@gibedigital.com]]></description>
                    <pubDate>Tue, 04 Nov 2025 08:39:27 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#122</guid>
                    <title>Skrift Issue #122</title>
                    <link>https://skrift.io/issues/#122</link>
                    <description><![CDATA[Featuring guest posts by Micha&amp;#235;l Latouche on &quot;The Umbraco Community Teams Visits: The Outcome of a Successful Open Source Collaboration&quot; and Nora Lahl on &quot;The Four Causes of Content Debt and How to Fix Them Before Your Next Rebuild&quot;]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/november/when-the-umbraco-community-responds/</guid>
                    <title>When the Umbraco Community responds.</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/november/when-the-umbraco-community-responds/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codeshttps://owain.codes/blogs/2025/november/when-the-umbraco-community-responds/</guid>
                    <title>When the Umbraco Community responds.</title>
                    <link>https://owain.codeshttps://owain.codes/blogs/2025/november/when-the-umbraco-community-responds/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blogs/2025/november/when-the-umbraco-community-responds/</guid>
                    <title>When the Umbraco Community responds.</title>
                    <link>https://owain.codes/blogs/2025/november/when-the-umbraco-community-responds/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2025/november/when-the-umbraco-community-responds/</guid>
                    <title>When the Umbraco Community responds.</title>
                    <link>https://owain.codes/blog/2025/november/when-the-umbraco-community-responds/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-expands-into-australia-and-new-zealand/</guid>
                    <title>Growing Together: Umbraco Expands into Australia and New Zealand</title>
                    <link>https://umbraco.com/blog/umbraco-expands-into-australia-and-new-zealand/</link>
                    <description><![CDATA[Umbraco expands into Australia &amp; New Zealand, bringing local leadership, support, and flexible digital experiences to the APAC region.]]></description>
                    <pubDate>Tue, 04 Nov 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/Umbraco.Community.Templates.UmBootstrap/17.0.0-rc1</guid>
                    <title>UmBootstrap 17.0.0-rc1 released</title>
                    <link>https://www.nuget.org/packages/Umbraco.Community.Templates.UmBootstrap/17.0.0-rc1</link>
                    <description><![CDATA[Released Today UmBootstrap 17.0.0.-rc1&#xA;https://www.nuget.org/packages/Umbraco.Community.Templates.UmBootstrap/17.0.0-rc1]]></description>
                    <pubDate>Mon, 03 Nov 2025 09:56:45 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://discord.com/channels/869656431308189746/1434835938219003983</guid>
                    <title>Request Protect v1.3.1 released including CIDR support for IP whitelisting</title>
                    <link>https://discord.com/channels/869656431308189746/1434835938219003983</link>
                    <description><![CDATA[We&#x2019;ve released Request Protect v1.3.1 for .NET projects.&#xA;&#xA;This update introduces CIDR support for IP whitelisting and lets you customise the access-denied response-giving you more control over how your environments stay protected.&#xA;&#xA;AI Summary: Request Protect provides flexible IP whitelisting, URL pattern matching, and query string authentication to help safeguard staging sites and preview environments. Get the latest version now on NuGet:&#xA;&#xA;https://www.nuget.org/packages/Moriyama.RequestProtect&#xA;https://www.nuget.org/packages/Moriyama.RequestProtect.Umbraco&#xA;&#xA;#PackageUpdate]]></description>
                    <pubDate>Mon, 03 Nov 2025 09:26:48 Z</pubDate>
                        <category>community</category>
                        <category>discord</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2025/november/has-the-umbraco-community-lost-its-spark/</guid>
                    <title>Has the Umbraco Community lost it&#x27;s Spark?</title>
                    <link>https://owain.codes/blog/2025/november/has-the-umbraco-community-lost-its-spark/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 03 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/</guid>
                    <title>Has the Umbraco Community lost it&#x27;s Spark?</title>
                    <link>https://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 03 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codeshttps://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/</guid>
                    <title>Has the Umbraco Community lost it&#x27;s Spark?</title>
                    <link>https://owain.codeshttps://owain.codes/blogs/2025/november/has-the-umbraco-community-lost-its-spark/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 03 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/november/has-the-umbraco-community-lost-its-spark/</guid>
                    <title>Has the Umbraco Community lost it&#x27;s Spark?</title>
                    <link>https://33b6c16b-3387-4d68-9090-4cb1f20ea7b3.azurewebsites.nethttps://owain.codes/blog/2025/november/has-the-umbraco-community-lost-its-spark/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 03 Nov 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.meetup.com/open-digital-pdx/events/311677037/?eventOrigin=group_upcoming_events</guid>
                    <title>Portland, Oregon Meet-up group launches their first Meetup event</title>
                    <link>https://www.meetup.com/open-digital-pdx/events/311677037/?eventOrigin=group_upcoming_events</link>
                    <description><![CDATA[Portland, Oregon Meet-up group: Hello UMB.FYI team -&#xA;&#xA;We are excited to share that we are working on creating a strong community presence in Portland, Oregon.&#xA;&#xA;Our first event &quot;Open for Business: How Open Digital Platforms Fuel Innovation and Profitability&#x201D; is on November 18th and features Mats Persson.&#xA;&#xA;Allen Smith recommended I get in touch to see if it&#x2019;s an event that can be promoted? I&#x2019;m happy to craft any kind of message needed.&#xA;&#xA;For more context, you can view our meet-up event page, or you can view this landing page:&#xA;&#xA;https://www.meetup.com/open-digital-pdx/events/311677037/?eventOrigin=group_upcoming_events&#xA;&#xA;https://www.wearediagram.com/event/umbraco/open-for-business-portland&#xA;&#xA;Thanks so much for your consideration!&#xA;&#xA;&#xA;Allison Casey&#xA;&#xA;Digital Marketing Director / Co-Owner&#xA;&#xA;&#xA;&#xA; [A black background with letters  Description automatically generated]&#xA;&#xA; acasey@wearediagram.com&lt;mailto:acasey@wearediagram.com&gt;&#xA; https://www.wearediagram.com/&#xA;   [signature_1342719604] &lt;https://twitter.com/wearediagram&gt; &#x202F; [signature_315527351] &lt;https://linkedin.com/company/wearediagram&gt; &#x202F;&#x202F;[signature_2219419470]&lt;https://www.instagram.com/heydiagram/&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;]]></description>
                    <pubDate>Fri, 31 Oct 2025 18:41:35 Z</pubDate>
                        <category>community</category>
                        <category>meetup</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=P3-3mxtv7RE</guid>
                    <title>umbraCoffee - Spooky Season</title>
                    <link>https://www.youtube.com/watch?v=P3-3mxtv7RE</link>
                    <description><![CDATA[Welcome to your monthly dose of #umbraCoffee&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;&#x200B;! Your hosts - Marcin and Callum - together with their guest(s) will drive through all of the community news and happenings in the Umbraco world. So... grab a cuppa, join us LIVE and enjoy!&#xA;&#xA;See ya?!&#xA;&#xA;---&#xA;&#x2615; Wanna buy us a coffee/pizza/donuts?&#xA;https://www.buymeacoffee.com/umbracoffee&#xA;---&#xA;&#xA;Notes:&#xA;&#xA;// TODO]]></description>
                    <pubDate>Fri, 31 Oct 2025 09:57:34 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.theestate</guid>
                    <title>uSkinnedSiteBuilder.StarterKit.TheEstate</title>
                    <link>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.theestate</link>
                    <description><![CDATA[Responsive Starter Kit &amp; Theme design add-on for uSkinned Site Builder demonstrating a fictional website for a luxury countryside Estate Hotel.]]></description>
                    <pubDate>Fri, 31 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.TwentyThree/releases/tag/v13.0.3</guid>
                    <title>Limbo.Umbraco.TwentyThree 13.0.3</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.TwentyThree/releases/tag/v13.0.3</link>
                    <description><![CDATA[Video picker for inserting TwentyThree videos in Umbraco.]]></description>
                    <pubDate>Thu, 30 Oct 2025 16:29:04 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/115463788632239549</guid>
                    <title>umbPublisher 1.2.0 is now available</title>
                    <link>https://umbracocommunity.social/@owaincodes/115463788632239549</link>
                    <description><![CDATA[umbPublisher 1.2.0 is now available. Some major code refactoring and a new settings interface to make it easier to setup and use. What is it? Do you use Obsidian and Umbraco? Would you like to push your notes to Umbraco as content? This is what this plugin does for you :) Want to see it in action? Come to my talk at #UmbracoInTheCity next week. #umbraco #obsidian]]></description>
                    <pubDate>Thu, 30 Oct 2025 15:20:08 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/release-candidate-umbraco-17/</guid>
                    <title>Umbraco 17: Release Candidate</title>
                    <link>https://umbraco.com/blog/release-candidate-umbraco-17/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 30 Oct 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@chilliapple/wordpress-vs-joomla-vs-umbraco-which-is-best-cms-for-your-website-074d2bed1c1d?source=rss------umbraco-5</guid>
                    <title>WordPress vs Joomla vs Umbraco&#x200A;&#x2014;&#x200A;Which is Best CMS for Your Website?</title>
                    <link>https://medium.com/@chilliapple/wordpress-vs-joomla-vs-umbraco-which-is-best-cms-for-your-website-074d2bed1c1d?source=rss------umbraco-5</link>
                    <description><![CDATA[&lt;div class=&quot;medium-feed-item&quot;&gt;&lt;p class=&quot;medium-feed-image&quot;&gt;&lt;a href=&quot;https://medium.com/@chilliapple/wordpress-vs-joomla-vs-umbraco-which-is-best-cms-for-your-website-074d2bed1c1d?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1092/0*jHgYgnCPNPgat5lj.jpg&quot; width=&quot;1092&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Choosing the best content management system can help you build and manage a successful website smoothly. The CMS plays a pivotal role in&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@chilliapple/wordpress-vs-joomla-vs-umbraco-which-is-best-cms-for-your-website-074d2bed1c1d?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Tue, 28 Oct 2025 09:52:52 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.TwentyThree/releases/tag/v13.0.2</guid>
                    <title>Limbo.Umbraco.TwentyThree 13.0.2</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.TwentyThree/releases/tag/v13.0.2</link>
                    <description><![CDATA[Video picker for inserting TwentyThree videos in Umbraco.]]></description>
                    <pubDate>Tue, 28 Oct 2025 08:41:15 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/umbracospark.bsky.social/post/3m4avcetazv2m</guid>
                    <title>UmbracoSpark seeking volunteers to MC at the 2026 event</title>
                    <link>https://bsky.app/profile/umbracospark.bsky.social/post/3m4avcetazv2m</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 28 Oct 2025 00:01:00 Z</pubDate>
                        <category>social</category>
                        <category>community</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/umbmanchester.bsky.social/post/3m4b2xarthb2i</guid>
                    <title>Join the Open Circle Discussion 6th November before Umbraco in the City Manchester</title>
                    <link>https://bsky.app/profile/umbmanchester.bsky.social/post/3m4b2xarthb2i</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 28 Oct 2025 00:01:00 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/115446893973859687</guid>
                    <title>Umbraco Tees Day happening this FRIDAY</title>
                    <link>https://umbracocommunity.social/@owaincodes/115446893973859687</link>
                    <description><![CDATA[To mark 1 week until #UmbracoInTheCity - there is a special Umbraco Tees Day happening this FRIDAY! Share photos of you in your Umbraco Swag and use the tag #UmbracoTeeshttps://isitumbracoteesday.today/]]></description>
                    <pubDate>Mon, 27 Oct 2025 16:17:42 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://uskinned.net/blog/new-starter-kits-and-themes-for-trades-events/</guid>
                    <title>Build Faster with Two New Starter Kits for Trades and Events</title>
                    <link>https://uskinned.net/blog/new-starter-kits-and-themes-for-trades-events/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 27 Oct 2025 11:32:27 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://dev.to/d_inventor/learning-tdd-by-doing-acceptance-testing-with-umbracos-management-api-4bff</guid>
                    <title>Learning TDD by doing: Acceptance testing with Umbraco&#x27;s management API</title>
                    <link>https://dev.to/d_inventor/learning-tdd-by-doing-acceptance-testing-with-umbracos-management-api-4bff</link>
                    <description><![CDATA[&lt;blockquote&gt;&#xA;&lt;p&gt;Remember: &#x1F534; Red, &#x1F7E2; Green, &#x267B;&#xFE0F; Refactor&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&quot;https://dev.to/d_inventor/learning-tdd-by-doing-is-umbracos-management-api-the-answer-to-acceptance-testing-3lpp&quot;&gt;In my previous post I speculated about end-to-end testing with Umbraco&#x27;s management API&lt;/a&gt;. I have tried it out and I can confirm that it works! In this post I will dive into what I wanted to achieve, how I did it and what challenges I faced along the way.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The goal&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;My goal was to automate content scenarios in order to provide a consistent initial state for acceptance tests. The result should be a suite of tests that interact with the website like an end user would.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you want to skip straight to the end result: check out my github repository with testing examples. I shared all my code there.&lt;/p&gt;&#xA;&#xA;&#xA;&lt;div class=&quot;ltag-github-readme-tag&quot;&gt;&#xA;  &lt;div class=&quot;readme-overview&quot;&gt;&#xA;    &lt;h2&gt;&#xA;      &lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg&quot; alt=&quot;GitHub logo&quot;&gt;&#xA;      &lt;a href=&quot;https://github.com/D-Inventor&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        D-Inventor&#xA;      &lt;/a&gt; / &lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco&quot; rel=&quot;noopener noreferrer&quot;&gt;&#xA;        automated-testing-in-umbraco&#xA;      &lt;/a&gt;&#xA;    &lt;/h2&gt;&#xA;    &lt;h3&gt;&#xA;      A working example of integration- and unittests with Umbraco. A demonstration of various concepts for testing your Umbraco website&#xA;    &lt;/h3&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&quot;ltag-github-body&quot;&gt;&#xA;    &#xA;&lt;div id=&quot;readme&quot; class=&quot;md&quot;&gt;&#xA;&lt;div class=&quot;markdown-heading&quot;&gt;&#xA;&lt;h1 class=&quot;heading-element&quot;&gt;Umbraco 16 automated testing setup&lt;/h1&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;This project is a fully functioning setup for automated testing with Umbraco 16. You can use this project as a reference or starting point to get started with testing on your Umbraco website. The tests are set up with Test Driven Development (TDD) in mind.&lt;/p&gt;&#xA;&lt;div class=&quot;markdown-heading&quot;&gt;&#xA;&lt;h2 class=&quot;heading-element&quot;&gt;Tools&lt;/h2&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The most important tools that are used in the automated tests are as follows:&lt;/p&gt;&#xA;&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;Name&lt;/th&gt;&#xA;&lt;th&gt;Description&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://xunit.net/?tabs=cs&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;xUnit v3&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;The testing framework. You can use any testing framework that you like though&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://nsubstitute.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;NSubstitute&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;Library for mocking. Any mocking library will work. This project doesn&#x27;t do extensive mocking, but for example &lt;code&gt;IPublishedValueFallback&lt;/code&gt; is a mandatory parameter for any published content item, even if you don&#x27;t actually use it. It&#x27;s just convenient to insert a mock.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;a href=&quot;https://testcontainers.com/&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Test Containers&lt;/a&gt;&lt;/td&gt;&#xA;&lt;td&gt;Automatically creates docker containers while running tests. It is used to create an empty SQL Server database that is automatically cleaned up after testing.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#x2026;&lt;/div&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&quot;gh-btn-container&quot;&gt;&lt;a class=&quot;gh-btn&quot; href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco&quot; rel=&quot;noopener noreferrer&quot;&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The journey&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There were two steps to my plan: First, I created a client that could communicate with Umbraco&#x27;s management API. Then, I built an interface on top of the client, so that I could describe the different kinds of content in easy-to-use models. I also created a small website to create a realistic test-case to use these tests on.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Step 1: Management API Client&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Umbraco&#x27;s management API is the interface that the Umbraco backoffice communicates with when you edit content. It is built with external integrations in mind.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;To create the API client, I used &lt;a href=&quot;https://github.com/RicoSuter/NSwag/wiki/nswagstudio&quot; rel=&quot;noopener noreferrer&quot;&gt;NSwagStudio&lt;/a&gt;. With this tool, I can consume the OpenAPI spec that Umbraco exposes and automatically generate a C# client. Although it didn&#x27;t go perfectly, the generated client was very usable and Umbraco appears to have designed very nice and clear models and endpoints. The generated C# code couldn&#x27;t compile right away, because for some reason it depended on a type &lt;code&gt;FileResponse&lt;/code&gt;, which doesn&#x27;t exist. Fortunately, creating an empty class was enough to make it work.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;NSwagStudio does not generate the authentication code, so I had to extend the generated client to include authentication. This was easy enough with &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/reference/management-api/external-access&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco&#x27;s documentation on external access&lt;/a&gt;. After this, I was able to write a crude script that could access the backoffice and produce some basic content items.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Step 2: A layer of convenience&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The client works, but preparing a scenario using the Management API Client alone takes a lot of code. My goal was to be able to describe a scenario in code the same way as I would describe it to a coworker. So before I got to work, I wrote down some example sentences that I would use to describe a test scenario:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Given a content page with a tag, when I go to the page, I see related content with the same tag.&lt;/li&gt;&#xA;&lt;li&gt;Given a content page with the content from our figma design, when I go to the page, I see the page just like in our design&lt;/li&gt;&#xA;&lt;li&gt;Given a news overview with many news articles, when I load more articles, I see the next N articles&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;I designed a test that would describe a simple scenario:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldDisplayTitleFromContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;&#xA;    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contentPage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scenario&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithContentPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My content page&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Visits&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contentPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// then&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My content page&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CurrentPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The focus here was to get the &#x27;given&#x27; part working. I used test-driven development to design and build this scenario builder. &lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco/tree/main/test/TestingExample.ManagementApiClient.UnitTests&quot; rel=&quot;noopener noreferrer&quot;&gt;The tests that I wrote are included in the github repository&lt;/a&gt;, so you can have a look at those if it interests you. The fun thing about test-driven development is that a test makes me think about how I&#x27;m going to use the code that I&#x27;m about to write.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In the end, I didn&#x27;t get exactly the design that I wanted, simply because &quot;I want content with a header&quot; is too vague, but I did get close. The test above ended up looking like this:&lt;br&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;div class=&quot;highlight js-code-highlight&quot;&gt;&#xA;&lt;pre class=&quot;highlight csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&#xA;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ShouldDisplayTitleFromContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;    &lt;span class=&quot;c1&quot;&gt;// given&lt;/span&gt;&#xA;    &lt;span class=&quot;n&quot;&gt;Scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Homepage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&#xA;        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Variation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invariant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithHeader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Welcome to the website&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;BuildAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;homepage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HomePageObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;NewPageAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Website&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// when&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;homepage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GoToAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&#xA;&#xA;    &lt;span class=&quot;c1&quot;&gt;// then&lt;/span&gt;&#xA;    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;homepage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToHaveTextAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Welcome to the website&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&#xA;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Instead of saying &quot;The homepage has a header&quot;, it now says &quot;The invariant content of the homepage has a header&quot;. It&#x27;s a bit longer, but also more explicit. It also allows you to specify cultures and variants. Very useful when you want to test multilingual sites or if you use segments to personalize content. Additionally, I couldn&#x27;t get around an explicit call to &lt;code&gt;BuildAsync&lt;/code&gt;, because I need a trigger to actually make the changes.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Challenges / difficulties&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Here are some of the things that surprised me, some things that I found difficult and some roadblocks that I came across while developing this.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Visual regression testing? Yes, but not in dotnet&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The biggest challenge and also my largest surprise, was the fact that there don&#x27;t seem to be any visual regression testing tools available for dotnet. Playwright for example supports comparing screenshots in the version for javascript / typescript, but does not support this for its dotnet version. I had no plans to create my own visual regression testing tool, so I had to give up. It might have been better to create my scenario builder in javascript / typescript, in retrospect.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This was somewhat disappointing for me. In my team at work, it is very important that our websites look like the designs that we make upfront. One of our success criteria is that we can recreate the design using content in Umbraco. Being able to match a browser screenshot with an export from our design software would have been very valuable.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Domains in Umbraco didn&#x27;t quite work&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Just before writing this blog, &lt;a href=&quot;https://github.com/umbraco/umbraco-cms/issues/20655&quot; rel=&quot;noopener noreferrer&quot;&gt;I submitted a bug report to Umbraco&lt;/a&gt;, because it appears that Umbraco has a stale caching layer on top of their domain service. When I ran the tests more than once or on multiple scenario&#x27;s, I would run into bad status codes because the domain was reserved for the content of the previous scenario, even though that content was deleted.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I worked around this issue by creating my own custom endpoint in the management api. &lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco/blob/main/src/TestingExample.Website/Testing/ClearScenarioController.cs&quot; rel=&quot;noopener noreferrer&quot;&gt;A sort of &quot;delete everything&quot; endpoint that deletes all the content in the content tree&lt;/a&gt; and makes sure that domains are deleted explicitly first. Although it&#x27;s not ideal, it&#x27;s a workable solution and shows how flexible the system as a whole is.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Content is inherently complex&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;My example on GitHub and the example code above is simple on purpose. String values are easy to model. But how do you model related media? Images with crops? What about a blocklist? These types of content are inherently complex and there is no single best answer. Blocklists have quite complex rules, &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/fundamentals/backoffice/property-editors/built-in-umbraco-property-editors/block-editor/block-level-variance&quot; rel=&quot;noopener noreferrer&quot;&gt;take this article in the Umbraco documentation about block-level-variance for example&lt;/a&gt;. Media has its own section in the backoffice and introduces an additional external dependency: the filesystem. Do I need to recreate all media items on every test as well? Or maybe I can simply reserve a set of media items in the backoffice and reuse them? It really depends on your use-case and I haven&#x27;t quite figured this out for myself yet.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Layers and abstractions&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;I found it difficult to assign clear responsibilities between the &lt;code&gt;ScenarioBuilder&lt;/code&gt; and the &lt;code&gt;DocumentClient&lt;/code&gt;. I had a gut-feeling that somewhere here is supposed to be a &quot;repository&quot;. Intuitively, the ScenarioBuilder seemed to play the role of repository, because it translated domain models into DTOs for the management api, but after building everything, the &lt;code&gt;IDocumentClient&lt;/code&gt; ended up looking more like a repository than the ScenarioBuilder. In the end, I couldn&#x27;t really make a proper case for either, so neither of these types turned into a repository. This once again shows how test-driven development helps you make better design choices. I didn&#x27;t need a repository, so I didn&#x27;t make one.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  final thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Most of all, I&#x27;m very excited that I managed to make it work. I have to admit that without visual regression testing, I&#x27;m not exactly sure how I&#x27;m going to use this technology. That doesn&#x27;t mean that it&#x27;s useless though. I feel like there is a lot of potential and I am very happy with how easy everything turned out to be.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Please check out &lt;a href=&quot;https://github.com/D-Inventor/automated-testing-in-umbraco&quot; rel=&quot;noopener noreferrer&quot;&gt;my repository in GitHub with testing examples&lt;/a&gt;. It includes everything that I talked about in this post. Let me know your thoughts: do you have extensive test suites for your Umbraco sites? What kind of tests do you find most valuable?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Thank you for reading and I&#x27;ll see you in my next blog! &#x1F60A;&lt;/p&gt;]]></description>
                    <pubDate>Mon, 27 Oct 2025 10:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@stvnhrlnd/115437571890720355</guid>
                    <title>Running Umbraco on a Fairphone 5</title>
                    <link>https://umbracocommunity.social/@stvnhrlnd/115437571890720355</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Sun, 26 Oct 2025 14:31:37 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://richardsoeteman.net/blog/visualise-umbraco-document-json-in-umbraco-content-section/</guid>
                    <title>Visualise Document JSON in Umbraco Content section</title>
                    <link>https://richardsoeteman.net/blog/visualise-umbraco-document-json-in-umbraco-content-section/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 24 Oct 2025 19:14:23 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.MediaPicker/releases/tag/v13.0.1</guid>
                    <title>Limbo.Umbraco.MediaPicker 13.0.1</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.MediaPicker/releases/tag/v13.0.1</link>
                    <description><![CDATA[Umbraco package that extends the functionality of Umbraco&#x27;s build-in media picker property editor.]]></description>
                    <pubDate>Fri, 24 Oct 2025 09:34:11 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/composable-regrets-lessons-from-the-first-wave/</guid>
                    <title>Composable Regrets: Lessons From the First Wave</title>
                    <link>https://umbraco.com/blog/composable-regrets-lessons-from-the-first-wave/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 24 Oct 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rivlo.friendlymembers</guid>
                    <title>Rivlo.FriendlyMembers</title>
                    <link>https://marketplace.umbraco.com/package/rivlo.friendlymembers</link>
                    <description><![CDATA[Friendly Members &#x2014; handy member management utilities for Umbraco 13.]]></description>
                    <pubDate>Fri, 24 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/umbraco.com/post/3m3wvuzptkq2n</guid>
                    <title>Umbraco Cms 16.3.3 released</title>
                    <link>https://bsky.app/profile/umbraco.com/post/3m3wvuzptkq2n</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 23 Oct 2025 23:01:00 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/finding-the-right-path-getting-more-involved-in-the-umbraco-community/</guid>
                    <title>Finding the Right Path: Getting More Involved in the Umbraco Community</title>
                    <link>https://umbraco.com/blog/finding-the-right-path-getting-more-involved-in-the-umbraco-community/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 22 Oct 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.Skyfish/releases/tag/v13.0.1</guid>
                    <title>Limbo.Umbraco.Skyfish 13.0.1</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.Skyfish/releases/tag/v13.0.1</link>
                    <description><![CDATA[Video picker for inserting Skyfish videos in Umbraco.]]></description>
                    <pubDate>Tue, 21 Oct 2025 18:00:05 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.multidomainemail</guid>
                    <title>DigitalWonderlab.MultiDomainEmail</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.multidomainemail</link>
                    <description><![CDATA[Streamline multi-domain umbraco forms submissions with domain-specific SMTP and reCAPTCHA configurations. The ultimate solution for multi-site Umbraco installations, designed specifically for agencies managing multiple client sites.]]></description>
                    <pubDate>Tue, 21 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/jacksorjacksor.bsky.social/post/3m3nbcukdx22h</guid>
                    <title>Umbraco Technical Writers Group returns 28th October</title>
                    <link>https://bsky.app/profile/jacksorjacksor.bsky.social/post/3m3nbcukdx22h</link>
                    <description><![CDATA[UmBloggers - Umbraco Technical Writers Group - returns with a co-writing event!&#xA;Tuesday 28th October, 6:30pm GMT.&#xA;We&#x27;ll be writing, then discussing, then writing, then discussing - all as a group, supporting one another&#x27;s ideas.&#xA;I&#x27;d post the Meetup link but AWS Things.&#xA;#technicalwriting #writers&#xA;&#xA;]]></description>
                    <pubDate>Mon, 20 Oct 2025 16:46:20 Z</pubDate>
                        <category>community</category>
                        <category>bluesky</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@24DaysInUmbraco/115405109108712894</guid>
                    <title>Write for 24 Days In Umbraco</title>
                    <link>https://umbracocommunity.social/@24DaysInUmbraco/115405109108712894</link>
                    <description><![CDATA[Have you built a great extension for #Umbraco? Or maybe you tried but failed miserably? Think anyone else could learn something from that process (spoiler: they can!) &#x2014; write it up in an article for this year&#x27;s 24 Days In Umbraco - apply here: https://24days.in/umbraco-cms/write-for-us/]]></description>
                    <pubDate>Mon, 20 Oct 2025 07:52:26 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.RecycleBin/releases/tag/v13.0.1</guid>
                    <title>Limbo.Umbraco.RecycleBin 13.0.1</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.RecycleBin/releases/tag/v13.0.1</link>
                    <description><![CDATA[Limbo.Umbraco.RecycleBin is small package for Umbraco that takes out the trash.]]></description>
                    <pubDate>Mon, 20 Oct 2025 07:45:41 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/mix-and-match-search-providers/</guid>
                    <title>Mix and match search providers</title>
                    <link>https://kjac.dev/posts/mix-and-match-search-providers/</link>
                    <description><![CDATA[A tutorial showcasing how multiple search providers can run simultaneously with Umbraco Search.]]></description>
                    <pubDate>Mon, 20 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.commerce.portal</guid>
                    <title>Umbraco.Commerce.Portal</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.commerce.portal</link>
                    <description><![CDATA[A drop in customer portal for Umbraco Commerce.]]></description>
                    <pubDate>Sat, 18 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=La8DwCMmvFE</guid>
                    <title>Umbraco upgrades &#x2013; Bump Digital</title>
                    <link>https://www.youtube.com/watch?v=La8DwCMmvFE</link>
                    <description><![CDATA[Umbraco upgrades &#x2013; Bump Digital - YouTube]]></description>
                    <pubDate>Fri, 17 Oct 2025 07:09:26 Z</pubDate>
                        <category>youtube</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbanchorpoint</guid>
                    <title>UmbAnchorPoint</title>
                    <link>https://marketplace.umbraco.com/package/umbanchorpoint</link>
                    <description><![CDATA[A property editor for easily managing css/flex alignment via 9 anchor points]]></description>
                    <pubDate>Fri, 17 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rivlo.licensing.ui</guid>
                    <title>Rivlo.Licensing.UI</title>
                    <link>https://marketplace.umbraco.com/package/rivlo.licensing.ui</link>
                    <description><![CDATA[Add-on for Rivlo Licensing, providing UI elements for displaying licence status.]]></description>
                    <pubDate>Fri, 17 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/rivlo.licensing</guid>
                    <title>Rivlo.Licensing</title>
                    <link>https://marketplace.umbraco.com/package/rivlo.licensing</link>
                    <description><![CDATA[Reusable licensing validator for Umbraco packages, with support for domain, version and expiry checks.]]></description>
                    <pubDate>Fri, 17 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/aaronsadler.dev/post/3m3d7wwxbkk24</guid>
                    <title>UmbNav V4.0.0-beta for Umbraco V17</title>
                    <link>https://bsky.app/profile/aaronsadler.dev/post/3m3d7wwxbkk24</link>
                    <description><![CDATA[What&#x27;s that I spy... UmbNav for #Umbraco V17?&#xA;&#xA;Yes, in deedy &#x1F601;&#xA;&#xA;Grab #UmbNav V4.0.0-beta0052 here:&#xA;&#xA;www.nuget.org/packages/Umb...&#xA;&#xA;]]></description>
                    <pubDate>Thu, 16 Oct 2025 16:56:54 Z</pubDate>
                        <category>bluesky</category>
                        <category>community</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://github.com/abjerner/Limbo.Umbraco.MultiNodeTreePicker/releases/tag/v13.0.2</guid>
                    <title>Limbo.Umbraco.MultiNodeTreePicker 13.0.2</title>
                    <link>https://github.com/abjerner/Limbo.Umbraco.MultiNodeTreePicker/releases/tag/v13.0.2</link>
                    <description><![CDATA[Umbraco package that extends the functionality of Umbraco&#x27;s build-in Multinode Treepicker property editor.]]></description>
                    <pubDate>Thu, 16 Oct 2025 13:09:33 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.FilterList/releases/tag/v13.0.0-alpha002</guid>
                    <title>Limbo.Umbraco.FilterList 13.0.0-alpha002</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.FilterList/releases/tag/v13.0.0-alpha002</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 16 Oct 2025 12:59:41 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/umbmanchester.bsky.social/post/3m3cemgen4t2w</guid>
                    <title>Umbraco In The City: Manchester is SOLD OUT</title>
                    <link>https://bsky.app/profile/umbmanchester.bsky.social/post/3m3cemgen4t2w</link>
                    <description><![CDATA[&#x1F389; We&#x2019;re SOLD OUT! &#x1F389;&#xA;Umbraco In The City: Manchester is full to the brim.&#xA;10 incredible talks. 80 amazing attendees.&#xA;It&#x2019;s gonna be a proper good one.&#xA;&#x1F4CD; 7 Nov &#xB7; x&#x2B;why Manchester&#xA;#UITC #Umbraco #MCRTech #WebDev&#xA;&#xA;]]></description>
                    <pubDate>Thu, 16 Oct 2025 08:54:45 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/codecabin.bsky.social/post/3m3b4gzvdgs2u</guid>
                    <title>Applications for CODECABIN 2026 in the US are now open</title>
                    <link>https://bsky.app/profile/codecabin.bsky.social/post/3m3b4gzvdgs2u</link>
                    <description><![CDATA[Feeling nostalgia for CodeCabin already or want to find out firsthand what the ultimate Umbraco retreat is all about? Applications for CodeCabin 2026 are now open! For the first time ever, join us in the US!&#xA;&#xA;Spots are limited; submit your application now!&#xA;&#xA;codecab.in&#xA;&#xA;#codecabin #h5yr #umbraco&#xA;&#xA;]]></description>
                    <pubDate>Thu, 16 Oct 2025 08:29:23 Z</pubDate>
                        <category>social</category>
                        <category>community</category>
                        <category>bluesky</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.countthings</guid>
                    <title>DigitalWonderlab.CountThings</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.countthings</link>
                    <description><![CDATA[A friendly Umbraco backoffice dashboard that shows counts of content, media, users, schema, forms and more.]]></description>
                    <pubDate>Thu, 16 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbnumericdropdown</guid>
                    <title>UmbNumericDropdown</title>
                    <link>https://marketplace.umbraco.com/package/umbnumericdropdown</link>
                    <description><![CDATA[A property editor that combines numeric &#x2B; dropdown into one]]></description>
                    <pubDate>Thu, 16 Oct 2025 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/115377438820620071</guid>
                    <title>The Umbraco 17 Beta is here</title>
                    <link>https://umbracocommunity.social/@umbraco/115377438820620071</link>
                    <description><![CDATA[&#x1F680; The #Umbraco 17 Beta is here! And we can&#x2019;t wait to get your contributions&#x1F4AA;&#x1F449; Read the blog for all the details (and how to access the Beta): https://umbraco.com/blog/umbraco-17-beta-is-out/]]></description>
                    <pubDate>Wed, 15 Oct 2025 09:20:24 Z</pubDate>
                        <category>hq</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-17-beta-is-out/</guid>
                    <title>Umbraco 17: Ready, Reliable, and Less Scary than It Sounds</title>
                    <link>https://umbraco.com/blog/umbraco-17-beta-is-out/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 15 Oct 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/uprofile-october-2025-helen-chaplin/</guid>
                    <title>uProfile October 2025 - Helen Chaplin</title>
                    <link>https://umbraco.com/blog/uprofile-october-2025-helen-chaplin/</link>
                    <description><![CDATA[Meet Helen Chaplin &#x2014; a passionate sustainability advocate and avid world traveller]]></description>
                    <pubDate>Wed, 15 Oct 2025 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/speckl-testing-a-human-first-approach-to-ai-assisted-development</guid>
                    <title>SPECKL: Testing a Human-First Approach to AI-Assisted Development</title>
                    <link>https://mattbrailsford.dev/speckl-testing-a-human-first-approach-to-ai-assisted-development</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 14 Oct 2025 14:14:01 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://cultiv.social/@sebastiaan/115372929546014805</guid>
                    <title>Umbraco launches new releases.umbraco.com</title>
                    <link>https://cultiv.social/@sebastiaan/115372929546014805</link>
                    <description><![CDATA[&#x1F680; New site launch!We&#x2019;ve replaced the old Download section on Our Umbraco with the shiny new https://releases.umbraco.com!The new home for Umbraco releases &#x2014; everything works again (including Compare &#x1F389;) and old links redirect smoothly.Soon to be open source &#x1F440;#Umbraco]]></description>
                    <pubDate>Tue, 14 Oct 2025 14:13:43 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.goldfinch.me/blog/taking-stripe-payments-in-umbraco-commerce</guid>
                    <title>Taking Stripe Payments in Umbraco Commerce</title>
                    <link>https://www.goldfinch.me/blog/taking-stripe-payments-in-umbraco-commerce</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 14 Oct 2025 09:56:20 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbracospark/115343976076913244</guid>
                    <title>Umbraco Spark Call for speakers is now open!</title>
                    <link>https://umbracocommunity.social/@umbracospark/115343976076913244</link>
                    <description><![CDATA[&#x1F6A8; Call for speakers is now open! Join us at Umbraco Spark in Bristol on March 20, 2026. We&#x27;re looking for tech enthusiasts eager to push the boundaries of the Umbraco CMS&#x1F4A5; &#x1F449; Share your insights: https://sessionize.com/umbraco-spark-2026/ #UmbracoSpark #Umbraco]]></description>
                    <pubDate>Tue, 14 Oct 2025 07:54:37 Z</pubDate>
                        <category>community</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/29</guid>
                    <title>[Breaking change]: Removal of backoffice tours</title>
                    <link>https://github.com/umbraco/Announcements/issues/29</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 14 Oct 2025 04:47:56 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/28</guid>
                    <title>[Breaking change]: Removal of `BackOfficeDevelopment` runtime mode and `InModelsBuilder` models builder mode due to deprecation of Razor runtime compilation support in .NET</title>
                    <link>https://github.com/umbraco/Announcements/issues/28</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 14 Oct 2025 04:42:03 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://www.johanreitsma.com/blogs/exploring-gitmcp-with-umbraco-repositories/</guid>
                    <title>Exploring GitMCP with Umbraco Repositories</title>
                    <link>https://www.johanreitsma.com/blogs/exploring-gitmcp-with-umbraco-repositories/</link>
                    <description><![CDATA[GitMCP lets you connect public GitHub repositories directly to AI tools like GitHub Copilot, Claude, Cursor, and VS Code MCP clients. In this article, I show how to use GitMCP with the Umbraco source code so you can ask AI real questions based on rea...]]></description>
                    <pubDate>Mon, 13 Oct 2025 20:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracare.net/blog/umbraco-session-timeout-configuration-and-security/</guid>
                    <title>Umbraco Session Timeout Configuration and Security</title>
                    <link>https://umbracare.net/blog/umbraco-session-timeout-configuration-and-security/</link>
                    <description><![CDATA[Sometimes even the simplest settings deserve a second look. Umbraco&#x2019;s session timeout appears straightforward in the documentation, but I decided to investigate what actually controls it and how far it can be safely tuned.]]></description>
                    <pubDate>Sun, 12 Oct 2025 12:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.johanreitsma.com/blogs/missing-umbracoexternallogin-data-after-migration-in-umbraco-13/</guid>
                    <title>Missing umbracoExternalLogin Data After Migration in Umbraco 13</title>
                    <link>https://www.johanreitsma.com/blogs/missing-umbracoexternallogin-data-after-migration-in-umbraco-13/</link>
                    <description><![CDATA[During a migration from Nested Content to Block List in Umbraco 13, I noticed that the `umbracoExternalLogin` table had suddenly become empty. The reason turned out to be missing external login provider settings in my local `appsettings.json`. In thi...]]></description>
                    <pubDate>Fri, 10 Oct 2025 12:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
    </channel>
</rss>