<?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>Wed, 17 Jun 2026 09:19:07 Z</lastBuildDate>
        <language>en</language>
        <generator>Umbraco</generator>
                <item>
                    <guid>https://jcdc.dev/blog/umbraco-package-automate-inspector</guid>
                    <title>Umbraco Package: Automate Inspector</title>
                    <link>https://jcdc.dev/blog/umbraco-package-automate-inspector</link>
                    <description><![CDATA[Explore everything Umbraco Automate has to offer with this new community package!]]></description>
                    <pubDate>Wed, 17 Jun 2026 07:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/my-codegarden-2026-experience/</guid>
                    <title>My Codegarden 2026 Experience</title>
                    <link>https://codeshare.co.uk/blog/my-codegarden-2026-experience/</link>
                    <description><![CDATA[My Codegarden 2026 experience: keynotes, talks, awards, and the moments that reminded me why this community matters. Nine years, still getting goosebumps.]]></description>
                    <pubDate>Tue, 16 Jun 2026 02:57:14 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://aaronsadler.dev/blog/2026/june/16/codegarden-2026-i-went-to-copenhagen-for-a-week</guid>
                    <title>Codegarden 2026: I went to Copenhagen for a week</title>
                    <link>https://aaronsadler.dev/blog/2026/june/16/codegarden-2026-i-went-to-copenhagen-for-a-week</link>
                    <description><![CDATA[My week in Copenhagen for Codegarden 2026: the MVP Summit, the launch of Umbraco Automate, the sessions that stood out, and why I keep coming back. &lt;a href=&quot;https://aaronsadler.dev/blog/2026/june/16/codegarden-2026-i-went-to-copenhagen-for-a-week&quot;&gt;Continue reading&lt;/a&gt;]]></description>
                    <pubDate>Tue, 16 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.contenttreedraganddrop</guid>
                    <title>Esatto.Umbraco.Backoffice.ContentTreeDragAndDrop</title>
                    <link>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.contenttreedraganddrop</link>
                    <description><![CDATA[Native HTML5 drag-and-drop for the Umbraco 17 backoffice Content tree. Adds three-zone (above / into / below) drop targets per row with optimistic same-parent reorder and full cross-parent move support. Pure App_Plugins package &#x2014; no C# code.]]></description>
                    <pubDate>Tue, 16 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umcontentgenerator</guid>
                    <title>umContentGenerator</title>
                    <link>https://marketplace.umbraco.com/package/umcontentgenerator</link>
                    <description><![CDATA[AI-powered content generation for Umbraco: create fully-populated Block Grid and Block List pages &#x2014; text and images &#x2014; base on template page and short promt, powered by OpenAI.]]></description>
                    <pubDate>Tue, 16 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://dev.to/luukpeters/tiptap-is-not-broken-your-expectations-are-5h8m</guid>
                    <title>TipTap is not broken. Your expectations are.</title>
                    <link>https://dev.to/luukpeters/tiptap-is-not-broken-your-expectations-are-5h8m</link>
                    <description><![CDATA[&lt;p&gt;Since the Umbraco 16 release, Umbraco ships only with the TipTap Rich Text Editor. This was unfortunately something that Umbraco was forced to do. TinyMce 7 has a license that is incompatible with the open source license of Umbraco and TinyMce 6 was going out of support. So an alternative had to be found.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco did a pretty good job at abstracting the rich text data. In Umbraco 15 both TinyMCE and TipTap were still present and exchangeable because of this abstraction. And arguably, the way you can set up your toolbars for the TipTap editor is superior to TinyMCE. But still, a new Rich Text Editor is a big change that presents real challenges.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;These challenges are most obvious when looking at the Umbraco forum&#x27;s &lt;a href=&quot;https://forum.umbraco.com/tag/tip-tap/78&quot; rel=&quot;noopener noreferrer&quot;&gt;tip-tap tag&lt;/a&gt;. Topics vary, but a few come up again and again:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Additional HTML tags getting added to the markup, like a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag inside a &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;The inability to add certain tags to TipTap, like &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags&lt;/li&gt;&#xA;&lt;li&gt;The inability to add styling to an element, for instance to create a link that looks like a button&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;These are valid challenges, especially if you&#x27;re upgrading from an existing TinyMCE setup. But this is also a good moment to ask two questions:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Why&lt;/strong&gt; do I want the same behaviour?&lt;/li&gt;&#xA;&lt;li&gt;And was the old behaviour actually any good to begin with?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  You don&#x27;t have to migrate everything at once&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before getting into that, it&#x27;s worth knowing that TinyMCE is still available as a community package for Umbraco 16&#x2B;. If you&#x27;re in the middle of a project, dealing with a large codebase, or just not ready to rethink your Rich Text Editor setup right now, that&#x27;s a valid escape hatch. Swap in the package, keep things running, and give yourself time to migrate properly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;But &quot;later&quot; should still be on the roadmap. The package is community maintained, not an official Umbraco product, so there are no guarantees around long-term support or compatibility with future Umbraco versions. Relying on it indefinitely carries the same risk as the situation Umbraco just came out of with TinyMCE 6.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So use it if you need to. Just don&#x27;t treat it as a permanent solution.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The RTE is not a page builder&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I&#x27;m not a big fan of giving editors too much freedom in a Rich Text Editor. Don&#x27;t get me wrong, RTEs are great and help editors a lot. But give too much rope and things go wrong fast. My preference is for RTEs to handle the basics: adding a link, making text bold or italic, maybe a table if the frontend deals with it gracefully on mobile.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;What I don&#x27;t like is image placement and scaling inside an RTE. Without proper processing this is a problem for performance and mobile layouts. I also don&#x27;t like HTML snippets or trying to add CSS classes to elements from within an editor. It&#x27;s error prone, and if something breaks, you need HTML knowledge to fix it. That&#x27;s not a realistic expectation for most editors.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So when people ask how to replicate that kind of behaviour in TipTap, I&#x27;d rather ask why they need it at all.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Stop fighting the editor, start working with Umbraco&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Instead of trying to make TipTap behave like TinyMCE did, consider using the tools Umbraco already gives you.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Need a link that looks like a button? Use a block inside the Rich Text Editor. TipTap has solid block support, and Umbraco exposes this well. You get a structured piece of content with its own view, its own properties, fully typed, fully renderable. An editor picks a &quot;Button&quot; block, fills in the label and the link and that&#x27;s it. No CSS classes, no raw HTML, no risk of someone pasting &lt;code&gt;style=&quot;color:red&quot;&lt;/code&gt; into a script tag three months later.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Need a pull quote, a callout, a code snippet with syntax highlighting? Same answer. Model it as a block. Render it properly. Keep the RTE itself lean.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The argument I keep hearing is that recreating all of this takes more time than just making the old thing work again. That&#x27;s sometimes true in the short term. But you&#x27;re building something more maintainable, more predictable and more editor-friendly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There&#x27;s another reason to think twice before pouring effort into recreating editor-specific behaviour: Umbraco has already switched its default Rich Text Editor once. It could happen again. If your implementation leans heavily on workarounds and editor-specific quirks, you&#x27;re setting yourself up for the same migration pain all over again. If instead your RTE is kept lean and the complex bits live in blocks with their own models and views, a future editor switch becomes much less of an event. The RTE just renders text. Everything interesting is already abstracted away.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What about the extra markup?&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; inside &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; is worth addressing separately, because it looks like a bug but it isn&#x27;t. TipTap is built on ProseMirror, which uses a strict document schema where every node needs to contain the right type of child node. For list items, that child is a block node and a paragraph is that block node. This is what makes editing reliable: without it, you can end up in situations where the cursor can&#x27;t be placed inside a list item at all, or where deleting an item breaks unexpectedly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This behaviour is not unique to TipTap. Any editor built on ProseMirror does the same thing, and that includes quite a few well-known ones. TinyMCE handled lists differently, but that doesn&#x27;t make TinyMCE&#x27;s approach the correct one.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If your frontend CSS breaks because of this, that&#x27;s a one-time fix, typically just adjusting margin or display rules on &lt;code&gt;li p&lt;/code&gt;. Once it&#x27;s done, you&#x27;re working with markup that is actually more structurally consistent than what TinyMCE produced.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Closing thought&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;A new editor is disruptive. But disruption is also a chance to reconsider decisions that were made years ago under different constraints, maybe with less knowledge of what Umbraco could do, or just because TinyMCE made it easy and easy felt like good enough.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;TipTap is not TinyMCE. It&#x27;s not trying to be. And once you stop expecting it to be, you might find it pushes you toward solutions that are cleaner, better structured and easier to hand off to an editor who has no idea what a DOM is.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Work with the grain of the tool, not against it.&lt;/p&gt;]]></description>
                    <pubDate>Mon, 15 Jun 2026 12:47:24 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/how-i-hacked-the-sticker-printers-at-codegarden-2026/</guid>
                    <title>How I Hacked the Sticker Printers at Codegarden 2026</title>
                    <link>https://codeshare.co.uk/blog/how-i-hacked-the-sticker-printers-at-codegarden-2026/</link>
                    <description><![CDATA[Discover how constraints sparked innovation at Codegarden 2026. I bypassed printer limitations using only my phone and creative problem-solving techniques.]]></description>
                    <pubDate>Mon, 15 Jun 2026 10:29:21 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nevitech.co.uk/blog/codegarden-2026-a-week-in-copenhagen-i-wont-forget/</guid>
                    <title>Codegarden 2026: A Week in Copenhagen I Won&#x27;t Forget</title>
                    <link>https://www.nevitech.co.uk/blog/codegarden-2026-a-week-in-copenhagen-i-wont-forget/</link>
                    <description><![CDATA[Just back from @umbraco.com Codegarden in Copenhagen, and what a week! &#x1F389;&#xA;&#xA;I was awarded Umbraco MVP 2026, did a demo in the keynote, and won the AI Package Award too. Still feels surreal. &#x1F3C6;&#xA;&#xA;Read more in the blog post.&#xA;&#xA;www.nevitech.co.uk/blog/codegar...&#xA;&#xA;#Umbraco #Codegarden&#xA;&#xA;]]></description>
                    <pubDate>Mon, 15 Jun 2026 10:19:36 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116753175751677147</guid>
                    <title>New release for umbPublisher 1.5.1</title>
                    <link>https://umbracocommunity.social/@owaincodes/116753175751677147</link>
                    <description><![CDATA[New release for umbPublisher 1.5.1Tags that are set in #obsidian can now be mapped to #umbraco property editor. Available on https://community.obsidian.md/plugins/umbpublisher]]></description>
                    <pubDate>Mon, 15 Jun 2026 08:43:36 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://dev.to/d_inventor/i-attended-umbraco-codegarden-2026-5dak</guid>
                    <title>I attended Umbraco Codegarden 2026!</title>
                    <link>https://dev.to/d_inventor/i-attended-umbraco-codegarden-2026-5dak</link>
                    <description><![CDATA[&lt;p&gt;Codegarden was last week in Copenhagen! I was there this time and here are my thoughts about the event. Previous time I attended was in 2023, so I will draw some comparisons to that year. It was a great experience and I left feeling generally excited and inspired.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Organization&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Just like previous time, the event was very well-organized. At Codegarden, you&#x27;re always well taken care of with lunch and dinner, great instructions, a little kick-off at the start of every day and a lot of spatial diversity. You always know what to expect.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I was very happy to see that the quiet area was easy to find and it was mentioned at the beginning of the event. The previous time I didn&#x27;t know that such a quiet area existed and I spoke out about that. It gave me comfort that I knew where to retreat to when I needed to. Additionally, there were many places to stand or sit outside and inside. The event was also a little shorter, which made it more manageable for me.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The location was absolutely ideal for the amount of people that attended. There were many attendees (about 700 they said, if I remember correctly), but at no point did the venue feel crowded. I could always easily get to where I wanted to go, there were no long queues and there was always enough seats for all the talks at every stage.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Talks&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I&#x27;ll admit that, on the previous Codegarden, I had the wrong expectations for the talks. Talks are obviously aimed at specific target audiences and I don&#x27;t belong to all of them. Some talks aim to inform, while others aim to inspire. Stories are not tutorials. Also, sometimes a talk doesn&#x27;t teach you something new, but simply validates what you were already doing.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So it was this time too. My focus is currently shifting slightly away from coding and more towards collaboration and I am looking for inspiration on how to deliver better software faster. I specifically wanted to attend &lt;em&gt;Debugging Teams: What High-Performing Developer Teams Do Differently&lt;/em&gt;, because it seemed relevant to me. Although I was hoping for a deep analysis in development process and teamwork strategies, I ended up learning about the importance of trust and of sharing the &quot;what&quot;, &quot;how&quot; and &quot;why&quot; of what we do. It surely gave me something to think about.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I listened to &lt;em&gt;Building Resilient Umbraco Integrations&lt;/em&gt;. A more tutorial-style demonstration with insightful information about what you need to think about when integrating with external services. The presentation included concrete code examples that gave concrete solutions to the presented problems. I personally already knew all of it, but it was nice to see anyway, because it confirms that I&#x27;m already doing it right.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A special mention goes to Semira Allen and Jason Wodicka with their talks &lt;em&gt;Drowning in Details: Turning TechTalk Into Everyday Language&lt;/em&gt; and &lt;em&gt;How (not) to mess up everything&lt;/em&gt;. Their talks were incredibly engaging and they were a joy to listen to. I had a good laugh and they were each inspiring in their own ways.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Some additional thoughts&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Copenhagen:&lt;/strong&gt; The fact that it was in Copenhagen was what made me consider it again. I am not big on traveling abroad and the trip to Odense and back was seriously daunting. Being able to get there with just the airplane made the whole trip so much better!&lt;br&gt;&#xA;&lt;strong&gt;AI:&lt;/strong&gt; I haven&#x27;t mentioned AI in this blog. That&#x27;s because I find the ethics of the application of AI at the moment very questionable. I am still working on forming a well-informed opinion. Umbraco and the community have showcased some very impressive work with AI.&lt;br&gt;&#xA;&lt;strong&gt;Umbraco awards:&lt;/strong&gt; In case you didn&#x27;t know it yet: we were nominated for an Umbraco award for &quot;Accessibility and Security&quot;! Even though we didn&#x27;t win the award, I believe that our submission was still the best. The winning solution had a more visible approach to accessibility, but we can show off a real certification of accessibility with end-user testimonies to back it up. The nomination is a nice validation of my team&#x27;s hard work.&lt;br&gt;&#xA;&lt;strong&gt;Umbraco in the bigger picture?&lt;/strong&gt; Right now I am learning from people like &lt;a href=&quot;https://www.youtube.com/@ModernSoftwareEngineeringYT&quot; rel=&quot;noopener noreferrer&quot;&gt;Dave Farley&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/@EmilyBache-tech-coach&quot; rel=&quot;noopener noreferrer&quot;&gt;Emily Bache&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/channel/UC3RKA4vunFAfrfxiJhPEplw&quot; rel=&quot;noopener noreferrer&quot;&gt;Derek Comartin&lt;/a&gt; about better software development practices. Some of it is more about architecture and some of it is about collaboration and process. I feel like these topics deserve more attention in the Umbraco community. I need a talk named &lt;em&gt;How I upgraded my mid-size web agency from feature branch and PR drag to real CI/CD&lt;/em&gt;. Maybe I get to give this talk myself one day, who knows.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Conclusion&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;It was a great event, and I&#x27;m very happy that I could be there. If it&#x27;s like this again next year, then I will be eager to attend. Thank you Umbraco for everything!&lt;/p&gt;]]></description>
                    <pubDate>Mon, 15 Jun 2026 07:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.sharedpreviewlink</guid>
                    <title>Esatto.Umbraco.Backoffice.SharedPreviewLink</title>
                    <link>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.sharedpreviewlink</link>
                    <description><![CDATA[Shareable preview links for Umbraco 17. Editors mint a tokenized URL via a workspace-action button; recipients open the URL externally and see the current draft (no backoffice login). Tokens are 7-day data-protected; recipient cookie scopes preview to the specific page for 5 minutes.]]></description>
                    <pubDate>Mon, 15 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/june/ocautomatemastodon-released/</guid>
                    <title>OC.Automate.Mastodon released</title>
                    <link>https://owain.codes/blog/2026/june/ocautomatemastodon-released/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 15 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/oc.automate.mastodon</guid>
                    <title>OC.Automate.Mastodon</title>
                    <link>https://marketplace.umbraco.com/package/oc.automate.mastodon</link>
                    <description><![CDATA[Mastodon connection type and action for Umbraco Automate. Post statuses to any Mastodon instance as part of an automation workflow.]]></description>
                    <pubDate>Mon, 15 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/across-the-himalayas-and-into-the-heart-of-umbraco/</guid>
                    <title>Across the Himalayas and Into the Heart of Umbraco</title>
                    <link>https://umbraco.com/blog/across-the-himalayas-and-into-the-heart-of-umbraco/</link>
                    <description><![CDATA[Meet Rashmita and Jasper, the founders of One Tech Nepal. Discover how they use their 100% Umbraco agency to fund IT education and libraries in remote Himalayan schools.]]></description>
                    <pubDate>Mon, 15 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://blog.aabech.no/archive/introducing-larsingerlogue-cv-5000/</guid>
                    <title>Introducing Larsingerlogue CV-5000</title>
                    <link>https://blog.aabech.no/archive/introducing-larsingerlogue-cv-5000/</link>
                    <description><![CDATA[&lt;h2&gt;Or how I gave Behringer FCV100 the reference power supply it deserves to use for CV-modulation with Korg Minilogue XD&lt;/h2&gt;&#xA;&lt;p&gt;&lt;em&gt;If you&#x27;re here to solve the problem and don&#x27;t care for a story, keep scrolling, you&#x27;ll get to schematics, breadboard pictures and an ingredient list.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;There&#x27;s also a &lt;a href=&quot;#video-montage&quot;&gt;short video montage&lt;/a&gt; for the even more impatient.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1080/intro-image.png&quot; alt=&quot;A logo with the text Larsingerlogue CV-5000 and an FCV100 connected to a gray box&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;This is quite a different post than I usually make, if I&#x27;m allowed to call it usually anymore at all. In any case, I&#x27;ve been playing the keys in a band for a couple of years and have yearned for a proper synth to supplement my (6) master keyboards. Recently I acquired my first ever analogue synthesizer. It only took 40 years for the dream to come through. I found a 15% cheaper second hand &amp;quot;unused&amp;quot; Korg Minilogue XD. But I only had damper pedals to use with it. Half the fun of synthesizers for me is varying the low pass filter cutoff. It&#x27;s what gives that cool swiping, buzzing or muffled sound depending on how you &amp;quot;turn&amp;quot; it. For that I needed another type of pedal, and not just a regular one.&lt;/p&gt;&#xA;&lt;p&gt;The Minilogue has a ton of hand-controlled potentiometer knobs we can use for a multitude of parameters, amongst others the filter cutoff. However it does not have a regular 1/4&amp;quot; jack slot for expression pedals like keyboards usually have. It has a damper slot, but only for CC64 on/off (sustain on and off). What we want is a full, preferably analogue, range from 0 to 100%. Expression pedals are excellent for this since they&#x27;re basically pot meters you turn with your foot.&lt;/p&gt;&#xA;&lt;p&gt;What the Minilogue comes with are two 1/8&amp;quot; jack &amp;quot;CV in&amp;quot; ports. So I browsed the interwebz for &amp;quot;CV pedal&amp;quot; and found a bunch of alternatives. Since I&#x27;m eyeing a Behringer Poly D come fall I thought the Behringer FCV100 V2 pedal seemed a good deal. It&#x27;s advertised as a volume pedal with a CV option, including reversing polarity. So I ordered one and excitedly unpacked it while the band leader ran out to buy the last 1/8&amp;quot; to 1/8&amp;quot; cable and a 1/8&amp;quot; to 1/4&amp;quot; adapter. I&#x27;d dutifully inserted a 9V battery and optimistically hooked everything up...&lt;/p&gt;&#xA;&lt;p&gt;Nothing!&lt;/p&gt;&#xA;&lt;p&gt;I tried both CV inputs, both polarities, tons of options on the minilogue, but still nothing.&lt;br /&gt;&#xA;When I got home - I plugged a 1/8&amp;quot; into the &lt;em&gt;input&lt;/em&gt; port which according to the interwebs should &amp;quot;power it on&amp;quot;. Still nothing from the CV output. And yes, I &lt;em&gt;had&lt;/em&gt; measured the voltage on the 9V battery.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: I have had no ambition doing electronics &amp;quot;right&amp;quot; with this project, nor writing a proper tutorial with a &amp;quot;right&amp;quot; way of doing it. This is a story about fiddling around and getting things to work. It is &lt;em&gt;not&lt;/em&gt; a tutorial in proper electronics, in fact almost the opposite. I have not bothered to look up facts or calculated things. This means people who know their way around electronics might find this article revolting. I&#x27;m sorry. It was a way for me to brush up on how things work and get something working quickly. DIY by fiddling around until it works. Again, sorry, but hopefully other learners might be inspired to both fiddle around &lt;em&gt;and&lt;/em&gt; learn to do the calculations. There&#x27;s even a gem of an insight in the end that only hit me while writing this post. &#x1F607;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2&gt;The research&lt;/h2&gt;&#xA;&lt;p&gt;According to the interwebz, someone claimed to have gotten CV output when they had plugged an unconnected 1/8&amp;quot; jack into input 2, but showed no proof or measurements. The next answer was someone who had done some due dilligence and measured the output. Apparently it only works with instruments that feed a reference voltage from its control voltage (CV) &lt;em&gt;input&lt;/em&gt; in order for the pedal to vary it.&lt;/p&gt;&#xA;&lt;p&gt;So I dug out my trusty old multimeter and started the process of dusting off the parts of my brain still remembering reading &lt;a href=&quot;https://www.goodreads.com/book/show/10796575-there-are-no-electrons&quot;&gt;&amp;quot;There are no Electrons: Electronics for Earthlings&amp;quot;&lt;/a&gt; ten plus years ago. I learned about what tip, ring and sleeve (TRS) for jack plugs are, and all the other variants. Measuring DC between the tip and sleeve of the unconnected side of the cable when pressing the pedal did absolutely nothing. Measuring resistance though showed a varying impedance of 0 through 50k&#x3A9;. If I flipped between TRS and RTS mode on the pedal it seemingly reversed the resistance on the ring, but the tip stayed fixed on 50k&#x3A9; in TRS mode. In any case, after some brain clearing with ChatGPT, I could conclude that the pedal in CV mode is &lt;em&gt;only&lt;/em&gt; a 50k&#x3A9; potmeter and does not provide any of the sweet power from the (now useless) 9V battery I had inserted.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Note: I&#x27;ve left the pedal in RTS mode. In order to use both and reverse polarity there&#x27;s more research and a V2 to be built. See closer to the end for a short rant about the factory presets in Minilogue XD.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;I figured there has to be a way to provide that power even if the synth doesn&#x27;t. So after a little more chatting with ChatGPT I figured I could dig through all my old cables and adapters to find an USB cable and adapter we&#x27;re not using. I should be able to &amp;quot;inject&amp;quot; it&#x27;s 5V DC voltage into the jack cable instead of relying on the synth to provide it. I dug through all my treasure drawers and found an USB mini-B 4-pin (?) to USB A cable and cut off the mini-B plug to reveal the two VCC/GND cables and two data cables. I found a couple of horribly old AC USB adapters and measured the DC from both via the cable&#x27;s red (VCC) and black (GND) wires. One gave 3.3V and the other gave 5V. I happily threw the overdue 3.3V one in the &amp;quot;to recycling&amp;quot; bin and rubbed my hands together over the other. (Throwing out one old device should score me at least one plus in the wife&#x27;s accounting system.)&lt;/p&gt;&#xA;&lt;h2&gt;Specifications&lt;/h2&gt;&#xA;&lt;p&gt;Of course it&#x27;s prudent to actually check the specs of the hardware you&#x27;re trying to shove a current into. So I dug up the details in the manuals, which to be honest are both quite a tad more than averagely vague about how to connect control voltage hardware.&lt;/p&gt;&#xA;&lt;p&gt;The Minilogue XD manual has the following gem of a CV mention:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;CV IN 1 and 2 jacks (3.5 mm monaural mini-phone jack; input voltage: &#x2013;5V to 5V in modulation mode, &#x2013;3V to 5V in CV/gate mode)&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;So I figured I&#x27;m safe proceeding with my 0-5V endeavour. We &lt;em&gt;are&lt;/em&gt; going to use it for modulation.&lt;/p&gt;&#xA;&lt;p&gt;The FCV100 manual has a similarly brief description of it&#x27;s &amp;quot;CV&amp;quot; output:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;1/4&amp;quot; TS - unbalanced Tip: Supply volt. from unit/instr. to be controlled: Impedance: 50kOhm Ring: Slider pick-up (CV dependend on ped. position, impedance: 0-50kOhm)&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;There&#x27;s some more specs around 2V and output impedance, but as far as I&#x27;ve understood that&#x27;s all about the volume control feature of the pedal. I have a hard time interpreting whether that sentence says &amp;quot;bring your own power&amp;quot; or &amp;quot;we&#x27;ll use the battery you inserted&amp;quot;.&lt;/p&gt;&#xA;&lt;p&gt;Having this information and some support from the [un]trusty LLM though, I pressed on to simulating the imagined setup.&lt;/p&gt;&#xA;&lt;h2&gt;Tinkering&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&quot;https://www.tinkercad.com/&quot;&gt;Tinkercad&lt;/a&gt; is an awesome tool for budding DYIers wanting to learn or experiment with electronics. Especially if starting to experiment with arduinos or other consumer grade microcontrollers. I could safely experiment with placement of the resistors and different resistance values and until I got the desired voltage output on the simulator&#x27;s multimeter.&lt;/p&gt;&#xA;&lt;p&gt;In place of the pedal I added a regular pot meter. It has three pins which is basically a 1:1 representation of the tip, ring and sleeve on a jack plug. The middle pin is the &amp;quot;wiper&amp;quot;, which I&#x27;d measured on the pedal to output a varying resistance between the ring and sleeve on the opposite end of the jack cable.&lt;br /&gt;&#xA;Back and forth with GPT and the multimeter for a while made me understand that the CV input on the Minilogue likely has an internal 1M&#x3A9; or larger resistor, just like a multimeter has. That&#x27;s how it&#x27;s able to read the voltage loss between the sides and thereby also receive the varying &amp;quot;control voltage&amp;quot; input from the pedal.&lt;/p&gt;&#xA;&lt;p&gt;Tinkercad also sports a USB connector and all the resistors you can dream of. And that&#x27;s the whole recipe for this &amp;quot;CV adapter&amp;quot;. It took some time to remember my URIs (Ohm&#x27;s law) and how things affect the dance of the electrons (or little green men if you prefer). To be honest I still don&#x27;t really know, but fiddling and discussing with GPT got me to something that works with a fair understanding. Mr. GPT had suggested a 50 k&#x3A9; resistor between the pedal and synth, but the simulations led me to choose a 10 k&#x3A9;. Again, the learned and/or dilligent would bother to calculate why.&lt;/p&gt;&#xA;&lt;p&gt;The ingredients for the simulation ended up as follows:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;Name&lt;/th&gt;&#xA;&lt;th&gt;Value&lt;/th&gt;&#xA;&lt;th&gt;Description&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&lt;tr&gt;&#xA;&lt;td&gt;USB Standard A&lt;/td&gt;&#xA;&lt;td&gt;-&lt;/td&gt;&#xA;&lt;td&gt;USB connector. It gives 5V in the simulator as expected.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Resistor A&lt;/td&gt;&#xA;&lt;td&gt;150 &#x3A9; &lt;br&gt;(several&amp;nbsp;tries)&lt;/td&gt;&#xA;&lt;td&gt;A resistor to limit the circuit current slightly from the USB output. Just to be precaucious.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Resistor B&lt;/td&gt;&#xA;&lt;td&gt;10 k&#x3A9;&lt;/td&gt;&#xA;&lt;td&gt;A resistor to protect and limit the current going into the synth. (We&#x27;d rather not blow it up...)&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Fake CV in port&lt;/td&gt;&#xA;&lt;td&gt;1 M&#x3A9;&lt;/td&gt;&#xA;&lt;td&gt;A resistor representing the internal impedance of the synth&#x27;s CV sensor and the tip (VCC) and sleeve (GND) of the jack plug.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Multimeter&lt;/td&gt;&#xA;&lt;td&gt;-&lt;/td&gt;&#xA;&lt;td&gt;A multimeter in resistance mode measuring the loss over the &quot;fake CV in port&quot; resistor.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Fake pedal&lt;/td&gt;&#xA;&lt;td&gt;50 k&#x3A9;&lt;/td&gt;&#xA;&lt;td&gt;A potentiometer representing the pedal and the tip (VCC), ring (CV out), and sleeve (GND) of the jack plug.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&lt;/table&gt;&#xA;&lt;hr /&gt;&#xA;&lt;p&gt;Here&#x27;s the breadboard layout:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1074/larsingerlogue-breadboard-tinkercad.png&quot; alt=&quot;A breadboard layout representing the pedal, extra parts and synth&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;I&#x27;m extremely discontent with how Tinkercad figured to lay out the schematic, but in any case I suppose it is correct. Why couldn&#x27;t the pot go straight to the 10k resistor? Oh well, here&#x27;s it is in all it&#x27;s jumbled glory:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1077/larsingerlogue-schematic-tinkercad.png&quot; alt=&quot;An electronic shematic representing the pedal, extra parts and synth&quot; /&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Prototyping and building in real life&lt;/h2&gt;&#xA;&lt;p&gt;Having the simulation show thumbs up I figured I should try it in real life and do some measurements before hooking anything up to my brand new synth. For some reason Tinkercad shows a tiny explosion on the USB connector when the simulation starts, but my adapter stood the test and I suspect there might be a bug with the implementation on Tinkercad. &#x1F607;&lt;/p&gt;&#xA;&lt;p&gt;I have both breadboards and a plethora of resistors lying around from my Arduino adventures last decade. Finding a breadboard and some resistors didn&#x27;t bide much problem. They&#x27;re all within arms length to my left. But this is where I started going back and forth between the breadboard and Tinkercad. Turns out real life didn&#x27;t give the same measurements as the simulation. Even if I used a PSU of 5.5V instead of a USB connector. My adapter gives 5.5V, remember?&lt;/p&gt;&#xA;&lt;p&gt;Here&#x27;s a little montage of all the crocodiles holding things together.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1073/larsingerlogue-breadboard-prototype.png&quot; alt=&quot;Picture montage showing a breadboard, wires, crocodile clips and a multimeter&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;After a while I had the right resistors to see 0-5V on the multimeter having hooked up the pedal to my shortest mangled jack-cable. I excitedly hooked up some crocodiles to the other half-jack and the breadboard, wearily shoved the plug into my synth and tried the pedal. Sucess! The LCD-display on the synth showed 0 to ... 97%??? So even if the simulator and my multimeter showed I output 5V from the pedal when 100% down the Minilogue expects more. Anyone knowledgeable with electronics will likely know why, but I merely suspect that it somehow &amp;quot;feels&amp;quot; the max input from the source is 5.5V. So it was out with the jack, out with the GND crocodile and in and out with misc initial resistors until I saw 0-100% CV input on the synth. A 150 &#x3A9; resistor turned out to be the sweet spot.&lt;/p&gt;&#xA;&lt;p&gt;I now had proof I could build this &amp;quot;CV-adapter&amp;quot;, so it was time for V1.&lt;/p&gt;&#xA;&lt;h2&gt;Assembling V1&lt;/h2&gt;&#xA;&lt;p&gt;Again from my Arduino-days I found a few circuit boards in my drawers. The smallest ones were 24x18 holes (7x3cm) - a little big. But incidentally I could spot in the corner of my eye something resembling an Arduino-case.  The 7x3 board fits perfectly within, and it&#x27;s a slim one just 1.5cm tall. Perfect!&lt;/p&gt;&#xA;&lt;p&gt;On to the soldering. Not that I lacked any space, but it&#x27;s always nice not having to cross wires. Enter a slightly unorthodox tool, my favorite program in the whole world: Excel. Who needs technical drawing software when you have colorable cells? I first drew up the layout on the left, but inspecting the casing I&#x27;d found I figured I&#x27;d rather flip it around. So I ended up going for the layout on the right.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1079/larsingerlogue_soldering_sketch.png&quot; alt=&quot;Excel sketches of the soldered circuit board&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;I&#x27;m extremely shaky, so soldering took its time. It&#x27;s somewhat of a Zen excercise I&#x27;ve never quite mastered. I also realized way too late (of course) that I&#x27;d been better off stretching a few more wires, but hey - it&#x27;s 10 years since last time I did anything like this.&lt;/p&gt;&#xA;&lt;p&gt;In any case, the first test with the multimeter and pedal showed a semi-constant 5.4V so I had to go back to the soldering board. A few connection tests with the trusty meter showed a lacking connection to ground from the pedal. Even with my new &amp;quot;computer glasses&amp;quot; &lt;em&gt;and&lt;/em&gt; a magnifyer on my little helper I couldn&#x27;t see that the amputated resistor leg I&#x27;d connected to ground wasn&#x27;t connected. But I could find the &lt;em&gt;spot&lt;/em&gt; where the connection ended, so I added kind of a highway of tin all the way. Success!&lt;/p&gt;&#xA;&lt;p&gt;Here&#x27;s how the circuitboard ended up.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1078/larsingerlogue-soldered-circuit.png&quot; alt=&quot;Picture showing of a soldered circuit board&quot; /&gt;&lt;/p&gt;&#xA;&lt;p&gt;The final stretch was to have the board rest well within the box. I was lucky to find some double sided tape in the kitchen, and since one side of the board already rested slighly on the USB and jack cables I chuckled as I cut off another 2.5cm piece of the now 4cm long USB mini cord. It fit perfectly. Tape on the case, tape on the board, and a short piece of USB mini cord as a pillow.&lt;/p&gt;&#xA;&lt;p&gt;Here&#x27;s the final box.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1076/larsingerlogue-final-box.png&quot; alt=&quot;Picture of a box connected to the FCV100&quot; /&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Final fun&lt;/h2&gt;&#xA;&lt;p&gt;Of course my box needed a name and a logo. It didn&#x27;t take many brain-iterations until I landed on Larsingerlogue CV-5000. It&#x27;s &lt;em&gt;my&lt;/em&gt; [Behr]&lt;strong&gt;inger&lt;/strong&gt; [FCV100 to Mini]&lt;strong&gt;logue&lt;/strong&gt;[ XD] &lt;strong&gt;CV-5&lt;strong&gt;[V * 1]&lt;/strong&gt;000&lt;/strong&gt;[mA adapter]. I turned to my age old friend &amp;quot;Impact&amp;quot; and quickly found a fitting gradient in Photoshop.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&quot;https://blog.aabech.no/media/1075/larsingerlogue-cv-5000.png&quot; alt=&quot;Larsingerlogue CV-5000 logo in fat lettering with a retro colored gradient.&quot; /&gt;&lt;/p&gt;&#xA;&lt;h3&gt;Video montage&lt;/h3&gt;&#xA;&lt;p&gt;I&#x27;d also shot a couple of videos and pictures during the process, so I amused myself by throwing together a short video of the whole process. It features the first built in patch of Minilogue XD and it&#x27;s sample sequence with me &amp;quot;playing&amp;quot; the filter cutoff on the now functioning pedal, and the &amp;quot;riser&amp;quot; delay time and depth using my free hand. Farily cool. And then a quite off-beat attempt at the Stranger Things theme as an RL demo of the final product. Check it out.&lt;/p&gt;&#xA;&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/nTTtX-T8_dE?si=M1ZUYMwnrk0mNLtL&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;&#xA;&lt;p&gt;I hope you&#x27;ve anjoyed following my little story of dipping my toes in electronics for music. There&#x27;s high potential for a V2 with better polarity support, and who knows what other ideas one might come up with for 100% &amp;quot;DIY&amp;quot; effects to shove into those CV inputs. There&#x27;s still one unused. &#x1F601;&lt;/p&gt;&#xA;&lt;h3&gt;Grief&lt;/h3&gt;&#xA;&lt;p&gt;My only grief, and that&#x27;s kind of connected to the single polarity of my adapter, is that &lt;em&gt;all&lt;/em&gt; of the built-in patches in the Minilogue XD are set to having CV 1 modulating the shape of VCO 1 from 0 to 100%, and CV 2 is set to filter cutoff from 0 to -100%. That means I open the filter by &amp;quot;closing&amp;quot; the pedal and close the filter by &amp;quot;opening&amp;quot; the pedal. Pedal to the floor means almost no audible sound.&lt;/p&gt;&#xA;&lt;h3&gt;Learning while blogging&lt;/h3&gt;&#xA;&lt;p&gt;In hindsight I think I just realized with writing this that 5V on the multimeter means (almost) &lt;em&gt;no&lt;/em&gt; power moves through the resistor, which is measured as 0V - while I&#x27;ve been reading 5V (loss) on the multimeter. That would explain the &amp;quot;weird&amp;quot; direction of the pot meter in the simulation that&#x27;d been bugging me. &#x1F923;&lt;/p&gt;&#xA;&lt;p&gt;But hey - adds to a fun story about learning and just jumping into the water.&lt;/p&gt;&#xA;&lt;p&gt;If you got this far I hope you had a chuckle, nodded or otherwise found this interesting.&lt;br /&gt;&#xA;Thanks a lot for reading, and have fun whatever endeavor led you to this post.&lt;/p&gt;]]></description>
                    <pubDate>Sun, 14 Jun 2026 21:24:09 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.dictionaryfiltervalues</guid>
                    <title>Esatto.Umbraco.Backoffice.DictionaryFilterValues</title>
                    <link>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.dictionaryfiltervalues</link>
                    <description><![CDATA[Extends the Umbraco 17 backoffice Dictionary section filter to also match translation VALUES, not just dictionary-item names. Useful for editors who remember a translated string but not the dictionary key. Adds one authenticated Management API endpoint and a backoffice entry point that re-points the built-in Dictionary collection&#x27;s data source at it &#x2014; no window.fetch patching, no anonymous endpoints.]]></description>
                    <pubDate>Sat, 13 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.customeditors</guid>
                    <title>Esatto.Umbraco.Backoffice.CustomEditors</title>
                    <link>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.customeditors</link>
                    <description><![CDATA[A library of reusable property editor UIs for the Umbraco backoffice. Ships an Encrypted Textbox editor (encrypts sensitive values at rest via ASP.NET Core Data Protection, masked on screen) and a Date Range editor (two inline calendars with start/end constraint, optional time and min/max bounds).]]></description>
                    <pubDate>Sat, 13 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=bmfrLgNfQ_g</guid>
                    <title>umbraCoffee - LIVE at #Codegarden 2026!</title>
                    <link>https://www.youtube.com/watch?v=bmfrLgNfQ_g</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 11 Jun 2026 10:52:11 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/im.umbraco.ai.local</guid>
                    <title>IM.Umbraco.AI.Local</title>
                    <link>https://marketplace.umbraco.com/package/im.umbraco.ai.local</link>
                    <description><![CDATA[Local LLM provider for Umbraco AI. Connects the Umbraco backoffice AI features (prompts, agents, Copilot, embeddings) to any OpenAI-compatible self-hosted server such as Ollama, LM Studio or vLLM &#x2014; no cloud account required.]]></description>
                    <pubDate>Thu, 11 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/ripplepreview</guid>
                    <title>RipplePreview</title>
                    <link>https://marketplace.umbraco.com/package/ripplepreview</link>
                    <description><![CDATA[See your actual pages while you edit them. Blocks render through your site&#x27;s own views, styles and scripts &#x2014; sections render whole with their children, carousels and animations run, and everything stays editable: hover to highlight a block inside the preview and click to open it.]]></description>
                    <pubDate>Thu, 11 Jun 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.aiagentmemory</guid>
                    <title>Umbraco.Community.AiAgentMemory</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.aiagentmemory</link>
                    <description><![CDATA[AI Agent Memory for Umbraco &#x2014; persistent memory &#x2B; learning layer for Umbraco&#x27;s AI agent stack. Editors give thumbs-up / thumbs-down feedback on agent runs via the AI Agent Feedback widget; the package indexes that feedback as searchable memory and injects it into subsequent runs so agents suppress false positives and reuse validated patterns. Composes on Umbraco.AI 1.8&#x2B; (vector store via Umbraco.AI.Search). Memory is off by default; per-agent opt-in via appsettings.json. Built for Umbraco v17&#x2B;.]]></description>
                    <pubDate>Thu, 11 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/the-umbraco-mvps-2026/</guid>
                    <title>The Umbraco MVPs 2026</title>
                    <link>https://umbraco.com/blog/the-umbraco-mvps-2026/</link>
                    <description><![CDATA[Meet our Umbraco MVPs for 2026. These are our Most Valuable People, people who have a passion for contributing to Umbraco and the Umbraco Community.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/bt.redirectmanager</guid>
                    <title>BT.RedirectManager</title>
                    <link>https://marketplace.umbraco.com/package/bt.redirectmanager</link>
                    <description><![CDATA[Modern URL redirect manager for Umbraco 13 and 17. Manage 301, 302, 404, and 410 redirects from a clean backoffice dashboard with regex support, CSV import/export, and a built&#x2011;in test tool.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-awards-winners-2026/</guid>
                    <title>Umbraco Awards Winners 2026 &#x1F3C6;</title>
                    <link>https://umbraco.com/blog/umbraco-awards-winners-2026/</link>
                    <description><![CDATA[Umbraco Awards 2026 winners are announced! Discover the outstanding projects and celebrate the amazing work within the Umbraco ecosystem.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.contenttreednd</guid>
                    <title>Esatto.Umbraco.Backoffice.ContentTreeDnd</title>
                    <link>https://marketplace.umbraco.com/package/esatto.umbraco.backoffice.contenttreednd</link>
                    <description><![CDATA[Native HTML5 drag-and-drop for the Umbraco 17 backoffice Content tree. Adds three-zone (above / into / below) drop targets per row with optimistic same-parent reorder and full cross-parent move support. Pure App_Plugins package &#x2014; no C# code.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.deploy.automate</guid>
                    <title>Umbraco.Deploy.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.deploy.automate</link>
                    <description><![CDATA[Umbraco Deploy support for Umbraco Automate (artifacts, service connectors, triggers and actions).]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-product-updatecodegarden-keynote-highlights-q2-2026/</guid>
                    <title>Umbraco Product Update/Codegarden Keynote Highlights, Q2 2026</title>
                    <link>https://umbraco.com/blog/umbraco-product-updatecodegarden-keynote-highlights-q2-2026/</link>
                    <description><![CDATA[Missed the Codegarden 2026 Keynote? Catch up on Umbraco&#x27;s Q2 product updates, including Umbraco Automate (new product!), CMS Elements, and Load Balancing for Cloud.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.automate</guid>
                    <title>Umbraco.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.automate</link>
                    <description><![CDATA[Umbraco Automate]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/flowcourier.umbraco.cookieconsent</guid>
                    <title>Flowcourier.Umbraco.CookieConsent</title>
                    <link>https://marketplace.umbraco.com/package/flowcourier.umbraco.cookieconsent</link>
                    <description><![CDATA[A comprehensive cookie consent solution for Umbraco CMS with Google Consent Mode v2 support, customizable categories, and statistics tracking.]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.forms.automate</guid>
                    <title>Umbraco.Forms.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.forms.automate</link>
                    <description><![CDATA[Umbraco Forms triggers and actions for Umbraco Automate]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/crumpled.nostalgid</guid>
                    <title>Crumpled.Nostalgid</title>
                    <link>https://marketplace.umbraco.com/package/crumpled.nostalgid</link>
                    <description><![CDATA[...]]></description>
                    <pubDate>Wed, 10 Jun 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.search</guid>
                    <title>Umbraco.AI.Search</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.search</link>
                    <description><![CDATA[AI-powered vector search provider for Umbraco CMS]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.commerce.automate</guid>
                    <title>Umbraco.Commerce.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.commerce.automate</link>
                    <description><![CDATA[Umbraco Commerce triggers and actions for Umbraco Automate]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.ai.automate</guid>
                    <title>Umbraco.AI.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.automate</link>
                    <description><![CDATA[Umbraco Automate integration for Umbraco.AI.Agent]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.engage.automate</guid>
                    <title>Umbraco.Engage.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.engage.automate</link>
                    <description><![CDATA[Umbraco Engage triggers and actions for Umbraco Automate]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.workflow.automate</guid>
                    <title>Umbraco.Workflow.Automate</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.workflow.automate</link>
                    <description><![CDATA[Umbraco Workflow triggers for Umbraco Automate]]></description>
                    <pubDate>Wed, 10 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/how-to-add-giscus-comments-to-an-umbraco-site/</guid>
                    <title>How to add Giscus Comments to an Umbraco Site</title>
                    <link>https://codeshare.co.uk/blog/how-to-add-giscus-comments-to-an-umbraco-site/</link>
                    <description><![CDATA[Add Giscus comments to your Umbraco site with GitHub Discussions. Free, open-source comment system with no backend, privacy-friendly, and developer-native.]]></description>
                    <pubDate>Mon, 08 Jun 2026 01:18:50 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.cms.search.core</guid>
                    <title>Umbraco.Cms.Search.Core</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.cms.search.core</link>
                    <description><![CDATA[Umbraco Search provides an abstraction for search services in Umbraco, allowing for implementations using search providers such as Examine.]]></description>
                    <pubDate>Mon, 08 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.enkelmedia.se/blogg/2026/6/7/codegarden-talk-hybrid-cache-changes-everything?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</guid>
                    <title>CodeGarden-talk: Hybrid Cache changes everything</title>
                    <link>https://www.enkelmedia.se/blogg/2026/6/7/codegarden-talk-hybrid-cache-changes-everything?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
                    <description><![CDATA[&lt;p&gt;I recently spent some time looking closer at the new &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/performance/caching/hybrid&quot;&gt;HybridCache&lt;/a&gt; implementation in Umbraco to prepare for my presentation at &lt;a href=&quot;https://codegarden.umbraco.com/&quot;&gt;CodeGarden 2026&lt;/a&gt;, and the short version is this: it changes more than just the cache layer.&lt;/p&gt;&#xA;&lt;p&gt;Historically, Umbraco has been very good at keeping published content available in memory. That has made many common developer patterns feel cheap and convenient. Things like traversing content trees, using `.Children()`, `.Descendants()` and resolving references have often just worked without us thinking too much about the cost.&lt;/p&gt;&#xA;&lt;p&gt;With HybridCache, that mental model changes. The cache is still there, but not everything necessarily lives in memory all the time.&lt;/p&gt;&#xA;&lt;h2&gt;The big win is scalability&lt;/h2&gt;&#xA;&lt;p&gt;The reason for this change is very easy to understand when looking at large Umbraco installations.&lt;/p&gt;&#xA;&lt;p&gt;Keeping everything in memory can work beautifully for small and medium-sized sites, but it becomes painful when a site has hundreds of thousands of pages, many languages, or very large content structures. Startup time, memory usage and hosting constraints can become real problems.&lt;/p&gt;&#xA;&lt;p&gt;HybridCache helps by reducing the memory footprint and making startup faster. Instead of loading everything into memory, Umbraco can seed important content and load other content when needed. That is a big architectural improvement.&lt;/p&gt;&#xA;&lt;h2&gt;The cost did not disappear&lt;/h2&gt;&#xA;&lt;p&gt;The interesting part, at least for me as a developer, is that the cost did not vanish. It moved.&lt;/p&gt;&#xA;&lt;p&gt;Some operations that used to feel almost free can now become more expensive, especially on larger sites. Traversing a large tree, filtering over many children, or resolving many references may trigger more cache lookups or database access than before.&lt;/p&gt;&#xA;&lt;p&gt;That does not mean HybridCache is bad. Quite the opposite. But it does mean we need to be more intentional about how we build things like menus, listings, sitemaps and search-driven pages.&lt;/p&gt;&#xA;&lt;h2&gt;Use the right tool for the job&lt;/h2&gt;&#xA;&lt;p&gt;One of my main takeaways is that we should stop treating the published content cache as the answer to everything.&lt;/p&gt;&#xA;&lt;p&gt;For search and filtering, Examine is often a much better fit. For menus, footers and repeated structures, runtime cache or prebuilt DTOs can be a good option. For sitemaps, adding the right data to the index (e.g. URLs) can be much more scalable than traversing the whole content tree.&lt;/p&gt;&#xA;&lt;p&gt;So the practical message is simple: measure, observe, and choose the right approach based on what the page actually needs to do.&lt;/p&gt;&#xA;&lt;p&gt;HybridCache gives Umbraco a better foundation for large sites, but it also makes performance patterns more visible. And honestly, that is probably a good thing.&lt;/p&gt;&#xA;&lt;h2&gt;Download the slides&lt;/h2&gt;&#xA;&lt;p&gt;I&#x2019;ve shared the full slide deck from the presentation if you want to dig into the examples, benchmarks and diagrams in more detail.&lt;/p&gt;&#xA;&lt;p&gt;[Download the slides link common soon]&lt;/p&gt;]]></description>
                    <pubDate>Sun, 07 Jun 2026 05:31:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/knowit.umbraco.translationmanager</guid>
                    <title>Knowit.Umbraco.TranslationManager</title>
                    <link>https://marketplace.umbraco.com/package/knowit.umbraco.translationmanager</link>
                    <description><![CDATA[Backoffice dashboard for auditing Umbraco translation dictionary keys against actual code usage. Surfaces unused keys, missing translations, and language coverage gaps across Razor, React, Vue, or any custom pattern. Includes a setup wizard, multi-source scanning, and bulk dictionary management.]]></description>
                    <pubDate>Fri, 05 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/knowit.umbraco.svgpicker</guid>
                    <title>Knowit.Umbraco.SvgPicker</title>
                    <link>https://marketplace.umbraco.com/package/knowit.umbraco.svgpicker</link>
                    <description><![CDATA[Property editor for Umbraco 17 that adds a visual SVG icon picker to the backoffice. Editors browse and search symbols from any configured SVG spritesheet. Stores the symbol ID as a plain string and renders as svg/use in Razor via a built-in Html.SvgIcon() helper.]]></description>
                    <pubDate>Fri, 05 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/codegarden-packing-list/</guid>
                    <title>Codegarden: What to bring (2026 edition!)</title>
                    <link>https://joe.gl/ombek/blog/codegarden-packing-list/</link>
                    <description><![CDATA[Wow, hasn&#x27;t time flown?! This weekend I&#x27;ll be boarding my plane to Denmark ahead of Codegarden 2026 so it&#x27;s about time we thought about packing our bags! But what to bring? If you&#x27;ve not been before, or if you&#x27;re forgetful like I am, I&#x27;ve written you a packing list...]]></description>
                    <pubDate>Thu, 04 Jun 2026 10:14:30 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.dynamicdave.nl/blog/why-2026-is-finally-the-year-i-m-going-to-codegarden/</guid>
                    <title>Why 2026 Is the Year to Finally Attend Codegarden</title>
                    <link>https://www.dynamicdave.nl/blog/why-2026-is-finally-the-year-i-m-going-to-codegarden/</link>
                    <description><![CDATA[Why 2026 Is the Year to Finally Attend Codegarden | DynamicDave]]></description>
                    <pubDate>Thu, 04 Jun 2026 07:55:39 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/security-advisory-june-4-2026-security-patch-for-umbracoai-is-now-available/</guid>
                    <title>Umbraco.AI Security Advisory, June 4, 2026</title>
                    <link>https://umbraco.com/blog/security-advisory-june-4-2026-security-patch-for-umbracoai-is-now-available/</link>
                    <description><![CDATA[Security patches are available for Umbraco.AI core package, versions 1.0.0 through 1.13.x versions fix a moderate vulnerability. Find the fix here]]></description>
                    <pubDate>Thu, 04 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://uskinned.net/blog/all-starter-kit-and-theme-add-ons-are-now-available-for-umbraco-17/</guid>
                    <title>All uSkinned Starter Kit and Themes Add-ons Out for Umbraco 17</title>
                    <link>https://uskinned.net/blog/all-starter-kit-and-theme-add-ons-are-now-available-for-umbraco-17/</link>
                    <description><![CDATA[All Starter Kit and Themes Add-ons Out for Umbraco 17&#xA;Every uSkinned Starter Kit and Theme add-on is now ready for Umbraco 17 and uSkinned Site Builder 7.]]></description>
                    <pubDate>Wed, 03 Jun 2026 09:42:08 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.jeroenbreuer.nl/blog/umbraco-openid-connect-example-with-lightweight-external-members/</guid>
                    <title>Umbraco OpenID Connect example with lightweight external members (Updated)</title>
                    <link>https://www.jeroenbreuer.nl/blog/umbraco-openid-connect-example-with-lightweight-external-members/</link>
                    <description><![CDATA[I updated the @umbraco OpenID Connect example to Umbraco 17.4.2 and added support for lightweight external members.Enabling it comes down to one flag: ExternalOnly = true. But there is one gotcha the docs don&#x27;t cover. Existing content-based members are not migrated automatically. If you want them to become lightweight external members, you need to delete them first and let Umbraco recreate them on the next login.Full details in the blog post below.https://www.jeroenbreuer.nl/blog/umbraco-openid-connect-example-with-lightweight-external-members/#Umbraco]]></description>
                    <pubDate>Wed, 03 Jun 2026 05:26:52 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.rdpmanager</guid>
                    <title>SplatDev.Umbraco.Plugins.RdpManager</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.rdpmanager</link>
                    <description><![CDATA[Umbraco Remote Desktop connection manager plugin &#x2014; store RDP configs and generate .rdp file downloads. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.countries</guid>
                    <title>SplatDev.Umbraco.Plugins.Countries</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.countries</link>
                    <description><![CDATA[Umbraco countries data plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 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.securitydashboard</guid>
                    <title>BlendInteractive.Umbraco.SecurityDashboard</title>
                    <link>https://marketplace.umbraco.com/package/blendinteractive.umbraco.securitydashboard</link>
                    <description><![CDATA[The Umbraco Security Dashboard is a package that provides a dashboard within the Umbraco backoffice to help identify potential security vulnerabilities in installed packages. It scans installed packages against known vulnerabilities and can report vulnerabilities to a central location via webhook.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.markup</guid>
                    <title>SplatDev.Umbraco.Markup</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.markup</link>
                    <description><![CDATA[HTML/markup helpers and extensions for Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.charlimit</guid>
                    <title>SplatDev.Umbraco.Plugins.CharLimit</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.charlimit</link>
                    <description><![CDATA[Character limit property editor for Umbraco &#x2014; enforces max length on text properties with live counter display.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.ispservices</guid>
                    <title>SplatDev.Umbraco.Plugins.IspServices</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.ispservices</link>
                    <description><![CDATA[Umbraco ISP/IP services utilities plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsticker</guid>
                    <title>SplatDev.Umbraco.Plugins.NewsTicker</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsticker</link>
                    <description><![CDATA[Scrolling news ticker plugin for Umbraco 13 (net8.0) and Umbraco 17 (net10.0). Configurable items, speed, and direction stored in the database.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.exceptionmanager</guid>
                    <title>SplatDev.Umbraco.Plugins.ExceptionManager</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.exceptionmanager</link>
                    <description><![CDATA[Umbraco exception handling middleware plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsletters</guid>
                    <title>SplatDev.Umbraco.Plugins.Newsletters</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsletters</link>
                    <description><![CDATA[Newsletter subscriber management, campaigns, and send tracking for Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.rsvp</guid>
                    <title>SplatDev.Umbraco.Plugins.Rsvp</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.rsvp</link>
                    <description><![CDATA[Umbraco RSVP event registration plugin with attendee management, capacity limits, waitlisting, and cancellation support. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.surveys</guid>
                    <title>SplatDev.Umbraco.Plugins.Surveys</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.surveys</link>
                    <description><![CDATA[Umbraco survey builder plugin with questions, multiple choice, free text, rating, and results dashboard. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.wordsapi</guid>
                    <title>SplatDev.Umbraco.Plugins.WordsApi</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.wordsapi</link>
                    <description><![CDATA[Umbraco Words API plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.examineextensions</guid>
                    <title>SplatDev.Umbraco.Plugins.ExamineExtensions</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.examineextensions</link>
                    <description><![CDATA[Examine search extensions for Umbraco &#x2014; additional query helpers, result transformers, and index utilities.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.seo</guid>
                    <title>SplatDev.Umbraco.Plugins.SEO</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.seo</link>
                    <description><![CDATA[Umbraco SEO plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.slider</guid>
                    <title>SplatDev.Umbraco.Plugins.Slider</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.slider</link>
                    <description><![CDATA[Image slider plugin for Umbraco &#x2014; stores slide data with EF Core, renders via configurable view component.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.customlogin</guid>
                    <title>SplatDev.Umbraco.Plugins.CustomLogin</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.customlogin</link>
                    <description><![CDATA[Umbraco custom login page plugin with branding support, SSO integration hooks and 2FA readiness. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.membertypes</guid>
                    <title>SplatDev.Umbraco.Plugins.MemberTypes</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.membertypes</link>
                    <description><![CDATA[Umbraco Member Type management plugin: create/edit custom member types with profile fields, custom properties and type templates. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.memberlogin</guid>
                    <title>SplatDev.Umbraco.Plugins.MemberLogin</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.memberlogin</link>
                    <description><![CDATA[Custom member login for Umbraco: login form, remember me, forgot password, lockout handling. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.socialmedia.share</guid>
                    <title>SplatDev.Umbraco.Plugins.SocialMedia.Share</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.socialmedia.share</link>
                    <description><![CDATA[Umbraco social sharing buttons plugin &#x2014; configurable platforms and share links. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.tweets</guid>
                    <title>SplatDev.Umbraco.Plugins.Tweets</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.tweets</link>
                    <description><![CDATA[Twitter/X feed display plugin for Umbraco 13 (net8.0) and Umbraco 17 (net10.0). Caches tweets locally and refreshes on a configurable schedule.]]></description>
                    <pubDate>Wed, 03 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.visitorcounter</guid>
                    <title>SplatDev.Umbraco.Plugins.VisitorCounter</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.visitorcounter</link>
                    <description><![CDATA[Umbraco visitor counter plugin: tracks unique and total site visitors, provides daily counts and a display widget. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Wed, 03 Jun 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.thefoundry</guid>
                    <title>uSkinnedSiteBuilder.StarterKit.TheFoundry</title>
                    <link>https://marketplace.umbraco.com/package/uskinnedsitebuilder.starterkit.thefoundry</link>
                    <description><![CDATA[The Foundry Starter Kit for uSkinned Site Builder for Umbraco CMS]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/june/speed-up-slow-deployments-to-umbraco-cloud/</guid>
                    <title>Speed up slow deployments to Umbraco Cloud</title>
                    <link>https://owain.codes/blog/2026/june/speed-up-slow-deployments-to-umbraco-cloud/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.mercadopago</guid>
                    <title>SplatDev.Umbraco.Plugins.Payments.MercadoPago</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.mercadopago</link>
                    <description><![CDATA[MercadoPago payment integration for Umbraco. Supports preference creation, payment status lookup, and webhook handling. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.d4sign</guid>
                    <title>SplatDev.Umbraco.Plugins.D4Sign</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.d4sign</link>
                    <description><![CDATA[D4Sign digital signature integration for Umbraco. Supports document upload, signer management, webhook processing, and a Lit 3 backoffice dashboard. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.livevideo</guid>
                    <title>SplatDev.Umbraco.Plugins.LiveVideo</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.livevideo</link>
                    <description><![CDATA[Live video embed for Umbraco &#x2014; YouTube Live, Twitch, and Vimeo live stream integration. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.videopreview</guid>
                    <title>SplatDev.Umbraco.Plugins.VideoPreview</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.videopreview</link>
                    <description><![CDATA[Video thumbnail preview for Umbraco &#x2014; auto-generate thumbnail URLs from YouTube, Vimeo, and Dailymotion video URLs. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.adminbar</guid>
                    <title>SplatDev.Umbraco.Plugins.AdminBar</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.adminbar</link>
                    <description><![CDATA[Fixed admin bar injected at the top of the page for logged-in backoffice users. Provides Edit Page, Preview and Publish shortcuts. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsletter</guid>
                    <title>SplatDev.Umbraco.Plugins.Newsletter</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.newsletter</link>
                    <description><![CDATA[Newsletter subscriber lists, campaigns, Mailgun bulk send, and stats tracking for Umbraco 17 (net10.0). Depends on SplatDev.Umbraco.Plugins.EmailTemplates for rendering.]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.restricted</guid>
                    <title>SplatDev.Umbraco.Plugins.Restricted</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.restricted</link>
                    <description><![CDATA[Umbraco Restricted Content plugin: member-only content gates, role-based access and paywall support using IPublicAccessService. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.emailnotifications</guid>
                    <title>SplatDev.Umbraco.Plugins.EmailNotifications</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.emailnotifications</link>
                    <description><![CDATA[Email template engine, Mailgun mail provider, newsletter campaigns, and member notifications for Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.passwordsettings</guid>
                    <title>SplatDev.Umbraco.Plugins.PasswordSettings</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.passwordsettings</link>
                    <description><![CDATA[Umbraco Password Settings plugin: complexity rules, expiration, history (prevent reuse), strength meter. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.pagseguro</guid>
                    <title>SplatDev.Umbraco.Plugins.Payments.PagSeguro</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.pagseguro</link>
                    <description><![CDATA[PagSeguro payment integration for Umbraco. Supports checkout session creation, transaction status lookup. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.enotassina</guid>
                    <title>SplatDev.Umbraco.Plugins.ENotAssina</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.enotassina</link>
                    <description><![CDATA[e-Not Assina electronic signature integration for Umbraco. Supports document creation, sequential signing, webhook processing, PDF download, and a Lit 3 backoffice dashboard. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.bancointer</guid>
                    <title>SplatDev.Umbraco.Plugins.Payments.BancoInter</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.payments.bancointer</link>
                    <description><![CDATA[Banco Inter payment integration for Umbraco. Supports Pix (immediate and due charges), Boleto com Pix, Banking (outbound Pix, boleto payments, balance/statement), and webhook handling. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.gdrp</guid>
                    <title>SplatDev.Umbraco.Plugins.Gdrp</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.gdrp</link>
                    <description><![CDATA[GDPR compliance plugin for Umbraco &#x2014; cookie consent banner, data export, and right to erasure requests. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.dictionarymanager</guid>
                    <title>SplatDev.Umbraco.Plugins.DictionaryManager</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.dictionarymanager</link>
                    <description><![CDATA[Dictionary import/export/CRUD manager for Umbraco &#x2014; full rewrite of the Umbraco 8 plugin using ILocalizationService. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#128</guid>
                    <title>Skrift Issue #128</title>
                    <link>https://skrift.io/issues/#128</link>
                    <description><![CDATA[Featuring guest posts by Pasang Tamang on &quot;From Umbraco 8 to Umbraco 17 in One Leap: uSync, Cursor, and the Umbraco MCP&quot; and Aaron Sawyer on &quot;Using Local LLMs wth Umbraco MCP&quot;]]></description>
                    <pubDate>Tue, 02 Jun 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.hiddencontent</guid>
                    <title>SplatDev.Umbraco.Plugins.HiddenContent</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.hiddencontent</link>
                    <description><![CDATA[Umbraco Hidden Content plugin: hide nodes from navigation and sitemaps while keeping them accessible by URL via the umbracoNaviHide property. Supports Umbraco 13 (net8.0) and Umbraco 17 (net10.0).]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.mailer</guid>
                    <title>SplatDev.Umbraco.Plugins.Mailer</title>
                    <link>https://marketplace.umbraco.com/package/splatdev.umbraco.plugins.mailer</link>
                    <description><![CDATA[Umbraco mailer plugin supporting Umbraco 13 (net8.0) and Umbraco 17 (net10.0)]]></description>
                    <pubDate>Tue, 02 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/in-memory-umbraco-search/</guid>
                    <title>In-memory Umbraco Search</title>
                    <link>https://kjac.dev/posts/in-memory-umbraco-search/</link>
                    <description><![CDATA[How to run Umbraco Search purely in-memory.]]></description>
                    <pubDate>Mon, 01 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/more-visibility-more-control-whats-new-in-umbraco-cloud/</guid>
                    <title>More Visibility, More Control. What&#x27;s New in Umbraco Cloud</title>
                    <link>https://umbraco.com/blog/more-visibility-more-control-whats-new-in-umbraco-cloud/</link>
                    <description><![CDATA[Discover what&#x2019;s new and coming next in Umbraco Cloud. Explore new features like performance insights, security updates, and dedicated scaling for agencies.]]></description>
                    <pubDate>Mon, 01 Jun 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/june/fixing-the-sort-order-in-content-pickers-in-umbraco/</guid>
                    <title>Fixing the sort order in Content Pickers in Umbraco</title>
                    <link>https://owain.codes/blog/2026/june/fixing-the-sort-order-in-content-pickers-in-umbraco/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 01 Jun 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/fastgooey.umbracoforms</guid>
                    <title>FastGooey.UmbracoForms</title>
                    <link>https://marketplace.umbraco.com/package/fastgooey.umbracoforms</link>
                    <description><![CDATA[Render Umbraco Forms into native iOS, macOS and Flutter viewing surfaces directly from your Umbraco projects.]]></description>
                    <pubDate>Mon, 01 Jun 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.reframe</guid>
                    <title>Umbraco.Community.AI.Reframe</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ai.reframe</link>
                    <description><![CDATA[Reframe image media to a new aspect ratio without leaving the Umbraco backoffice. A Reframe view on every Image media item lets editors retarget a picture either by Crop (instant, free, geometric) or by AI Outpaint (OpenAI gpt-image-2 invents the new edges so no content is lost). Preview before/after, then Accept to save the result as a new media item &#x2014; the original is never changed. Outpaint requires your own OpenAI API key and incurs OpenAI usage cost; Crop is free.]]></description>
                    <pubDate>Sat, 30 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://medium.com/@nomansiddiquins1/sso-in-umbraco-17-azure-ad-back-office-login-with-openid-connect-549a0e5f91e5?source=rss------umbraco-5</guid>
                    <title>SSO in Umbraco 17: Azure AD Back Office Login with OpenID Connect</title>
                    <link>https://medium.com/@nomansiddiquins1/sso-in-umbraco-17-azure-ad-back-office-login-with-openid-connect-549a0e5f91e5?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/@nomansiddiquins1/sso-in-umbraco-17-azure-ad-back-office-login-with-openid-connect-549a0e5f91e5?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/672/1*WGXVMKwKTrCDMQum52uvww.png&quot; width=&quot;672&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;A step-by-step guide to wiring up Azure Active Directory as an external login provider for the Umbraco back office &amp;#x2014; with auto-linking&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@nomansiddiquins1/sso-in-umbraco-17-azure-ad-back-office-login-with-openid-connect-549a0e5f91e5?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Fri, 29 May 2026 18:47:06 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=cCqq76ItuB4</guid>
                    <title>umbraCoffee - Agents, Codegarden, Tees</title>
                    <link>https://www.youtube.com/watch?v=cCqq76ItuB4</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;Today we&#x27;re joined by Adam Shallcross, CEO &amp; Founder of Cogworks &#x2013; and curator oft the Human Vibe Loop newsletter.&#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, 29 May 2026 09:02:18 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@lee/116654381716004252</guid>
                    <title>Contentment 7.0.0-alpha001 for Umbraco 18 RC out now</title>
                    <link>https://umbracocommunity.social/@lee/116654381716004252</link>
                    <description><![CDATA[#umbraco #Contentment 7.0.0-alpha001https://github.com/leekelleher/umbraco-contentment/releases/tag/7.0.0-alpha001TL;DR, want to try out Umbraco 18 with Contentment this weekend?Is it stable? Probably!Does it have bugs? Nah!How can you help? Test it out and feedback!]]></description>
                    <pubDate>Fri, 29 May 2026 06:23:49 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116652325620551265</guid>
                    <title>Umbraco Forums #h5yr now display on HFYR.com</title>
                    <link>https://umbracocommunity.social/@owaincodes/116652325620551265</link>
                    <description><![CDATA[Pleased to announce that now, if you give a #h5yr on the Umbraco Forum, that post will be displayed on H5YR.com Thanks @sebastiaan for helping get this setup. #umbraco]]></description>
                    <pubDate>Thu, 28 May 2026 13:00:42 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@UmbHost/116651510454937905</guid>
                    <title>UmbHost launches automated Umbraco upgrades</title>
                    <link>https://umbracocommunity.social/@UmbHost/116651510454937905</link>
                    <description><![CDATA[Automated Umbraco upgrades, sorted. GitHub Dependabot handles the dependency PRs, GreenStack handles the deploy. Here&#x27;s how you can do it &#x1F447;https://umbhost.net/blog/2026/05/building-your-own-umbraco-cloud-style-auto-upgrades-with-github-dependabot#Umbraco #dotnet #DevOps #GreenStack]]></description>
                    <pubDate>Thu, 28 May 2026 09:35:45 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/umbraco-18-release-candidate/</guid>
                    <title>Umbraco 18 Release Candidate</title>
                    <link>https://umbraco.com/blog/umbraco-18-release-candidate/</link>
                    <description><![CDATA[Discover and test the Umbraco 18 Release Candidate, featuring Elements for reusable content, typed OpenAPI schemas,]]></description>
                    <pubDate>Thu, 28 May 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.jeroenbreuer.nl/blog/how-i-let-cursor-build-an-azure-search-provider-for-umbraco-search/</guid>
                    <title>How I let Cursor build an Azure Search provider for Umbraco Search</title>
                    <link>https://www.jeroenbreuer.nl/blog/how-i-let-cursor-build-an-azure-search-provider-for-umbraco-search/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 27 May 2026 13:57:13 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/lantern.umbraco</guid>
                    <title>Lantern.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/lantern.umbraco</link>
                    <description><![CDATA[Add the Lantern bug-tracking widget to every page of your Umbraco site in seconds. Configure your embed key from the backoffice &#x2014; no template changes needed.]]></description>
                    <pubDate>Wed, 27 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/may/codegarden-2026-not-long-now/</guid>
                    <title>Codegarden 2026 - not long now!</title>
                    <link>https://owain.codes/blog/2026/may/codegarden-2026-not-long-now/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 27 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://medium.com/@contacts_81271/umbraco-vs-wordpress-b180bf6af576?source=rss------umbraco-5</guid>
                    <title>Umbraco vs WordPress</title>
                    <link>https://medium.com/@contacts_81271/umbraco-vs-wordpress-b180bf6af576?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/@contacts_81271/umbraco-vs-wordpress-b180bf6af576?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1672/1*rTkBaNhm67woupfcRsK7FA.png&quot; width=&quot;1672&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;Die Wahl des richtigen Web-Content-Management-Systems gleicht heute einer strategischen Richtungsentscheidung, die ma&amp;#xDF;geblichen Einfluss&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@contacts_81271/umbraco-vs-wordpress-b180bf6af576?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Tue, 26 May 2026 10:17:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/browsing-umbraco-logs-in-kudu/</guid>
                    <title>Browsing Umbraco Logs in Kudu</title>
                    <link>https://skttl.dev/blog/browsing-umbraco-logs-in-kudu/</link>
                    <description><![CDATA[Browsing Umbraco Logs in Kudu | skttl.dev]]></description>
                    <pubDate>Tue, 26 May 2026 06:09:21 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/upreviewshare</guid>
                    <title>uPreviewShare</title>
                    <link>https://marketplace.umbraco.com/package/upreviewshare</link>
                    <description><![CDATA[Securely share unpublished draft content with external stakeholders via time-limited, view-capped, and PIN-protected preview links. Renders through actual Umbraco templates.]]></description>
                    <pubDate>Tue, 26 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=wwQBvBDGhPE</guid>
                    <title>Umbraco Search Explained | Kenn Jacobsen | Umbraco India Meetup</title>
                    <link>https://www.youtube.com/watch?v=wwQBvBDGhPE</link>
                    <description><![CDATA[Featuring Kenn Jacobsen&#xA;Kenn is a 4x Umbraco MVP and a long-time contributor to the Umbraco community, with more than 10 years of experience building solutions with Umbraco and .NET.&#xA;&#xA;&#x1F4FA; Watch the full session recording and learn what&#x2019;s coming next for search in the Umbraco ecosystem.&#xA;&#xA;&#x1F30D; Join us for upcoming Umbraco India User Group events and community updates.&#xA;&#x1F517;https://www.meetup.com/umbraco-india-user-group&#xA;&#x1F517;https://www.linkedin.com/groups/14558029]]></description>
                    <pubDate>Thu, 21 May 2026 09:52:54 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/m1sterpl0w.umbraco.accessrestriction</guid>
                    <title>M1sterPl0w.Umbraco.AccessRestriction</title>
                    <link>https://marketplace.umbraco.com/package/m1sterpl0w.umbraco.accessrestriction</link>
                    <description><![CDATA[An Umbraco package that restricts access to your site (or specific paths) by IP address whitelist. Manage whitelisted IPs and restricted paths from a dashboard in the Umbraco backoffice, with optional hardcoded entries via appsettings.json.]]></description>
                    <pubDate>Thu, 21 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@UmbHost/116606538139138040</guid>
                    <title>UmbHost.Tables for Umbraco has had an update</title>
                    <link>https://umbracocommunity.social/@UmbHost/116606538139138040</link>
                    <description><![CDATA[UmbHost.Tables for #Umbraco has had an update courtesy of Mike Masey!This replaces the custom RTE with the fully featured tiptap RTE with all the configuration options you would expect.View source now opens in a modal windowThere is also a range of accessibility and UX improvementsMany thanks and a huge #h5yr for this PR!Grab it here:https://www.nuget.org/packages/UmbHost.Tables/17.1.0]]></description>
                    <pubDate>Wed, 20 May 2026 10:56:20 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://github.com/prjseal/Start-With-Markdown/</guid>
                    <title>Start With Markdown</title>
                    <link>https://github.com/prjseal/Start-With-Markdown/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 20 May 2026 06:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://discord.com/channels/869656431308189746/882981290662580264/1506219000525225994</guid>
                    <title> Clean starter kit for Umbraco 18 out in Beta</title>
                    <link>https://discord.com/channels/869656431308189746/882981290662580264/1506219000525225994</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 20 May 2026 06:00:00 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>discord</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/pragmaticit.umbraco.virtualmembers</guid>
                    <title>PragmaticIT.Umbraco.VirtualMembers</title>
                    <link>https://marketplace.umbraco.com/package/pragmaticit.umbraco.virtualmembers</link>
                    <description><![CDATA[Passwordless, database-free member authentication for Umbraco CMS. Protect content nodes with email-only, OTP, or MFA login &#x2014; members loaded from plain CSV files, no Umbraco Members database records required.]]></description>
                    <pubDate>Wed, 20 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116601888665115697</guid>
                    <title>UmbracoTees day May 29th</title>
                    <link>https://umbracocommunity.social/@owaincodes/116601888665115697</link>
                    <description><![CDATA[Can you believe we haven&#x27;t had an #UmbracoTees day this year yet? Well, let&#x27;s change that!! Get your Umbraco Swag on, share your photos with #umbracoTees and let&#x27;s see you all celebrate all things #umbraco May 29th 2026!https://isitumbracoteesday.today/]]></description>
                    <pubDate>Tue, 19 May 2026 15:13:52 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=DcxmRjoFZwc</guid>
                    <title>Start With Markdown</title>
                    <link>https://www.youtube.com/watch?v=DcxmRjoFZwc</link>
                    <description><![CDATA[Learn about my new open source content publishing workflow called Start With Markdown&#xA;&#xA;It&#x27;s a simple workflow which triggers when you create or modify a markdown file in the posts folder of your github repo.&#xA;&#xA;When it triggers it calls some endpoints on your CMS to create the content and it returns the edit, preview and publish URLs for you.&#xA;&#xA;Here is the link to the main project that you fork and setup to call your CMS endpoints&#xA;https://github.com/prjseal/Start-With-Markdown/&#xA;&#xA;And here is the link to the demo Umbraco project and supporting libraries with the custom resolvers for unsplash and things like that to get it working with the Clean starter kit&#xA;https://github.com/prjseal/Start-With-Markdown-Umbraco-Libraries]]></description>
                    <pubDate>Tue, 19 May 2026 14:17:30 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://dev.to/lauraneto/umbraco-18-and-openapi-a-heads-up-for-extension-developers-1k7</guid>
                    <title>Umbraco 18 and OpenAPI: a heads-up for extension developers</title>
                    <link>https://dev.to/lauraneto/umbraco-18-and-openapi-a-heads-up-for-extension-developers-1k7</link>
                    <description><![CDATA[&lt;p&gt;Time to upgrade your extension to Umbraco 18?&lt;br&gt;&#xA;A friendly heads up, the OpenAPI generation got an overhaul.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;Microsoft.AspNetCore.OpenApi&lt;/code&gt; is Umbraco&#x27;s new library of choice for OpenAPI document generation, replacing &lt;code&gt;Swashbuckle.AspNetCore&lt;/code&gt;. It ships with ASP.NET Core and is maintained by Microsoft alongside the framework, which means one fewer third-party dependency to worry about.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The trade-off: if your extension wired up its own OpenAPI document on v17, it won&#x27;t compile against v18. The Swashbuckle types it relied on are gone.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Two paths forward:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Migrate to Microsoft OpenAPI&lt;/strong&gt;. Two options:&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Use the &lt;code&gt;AddBackOfficeOpenApiDocument&lt;/code&gt; extension method&lt;/strong&gt;: Umbraco&#x27;s new helper. What the v18 template uses. Covers most extensions.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Use Microsoft&#x27;s &lt;code&gt;AddOpenApi&lt;/code&gt; method directly&lt;/strong&gt;: call &lt;code&gt;Microsoft.AspNetCore.OpenApi&lt;/code&gt; yourself. For full control.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Keep using Swashbuckle&lt;/strong&gt; &lt;em&gt;(not recommended)&lt;/em&gt;. Install &lt;code&gt;Swashbuckle.AspNetCore&lt;/code&gt; in your own extension, wire it up, then plug your document into Umbraco&#x27;s Swagger UI via &lt;code&gt;AddOpenApiDocumentToUi(...)&lt;/code&gt;. When rewriting isn&#x27;t an option right now.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&#x2139;&#xFE0F; &lt;strong&gt;Note:&lt;/strong&gt; the OpenAPI spec URL also changed from &lt;code&gt;/umbraco/swagger/{documentName}/swagger.json&lt;/code&gt; to &lt;code&gt;/umbraco/openapi/{documentName}.json&lt;/code&gt;. If you generate a client from the spec (e.g. via the v17 extension template&#x27;s &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;generate-client&lt;/code&gt; script), update the URL before regenerating.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Use the AddBackOfficeOpenApiDocument extension method&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We added &lt;code&gt;AddBackOfficeOpenApiDocument()&lt;/code&gt; in v18 to take the boilerplate out of registering an OpenAPI document. It&#x27;s an extension method on &lt;code&gt;IUmbracoBuilder&lt;/code&gt; that wires up Umbraco&#x27;s sensible defaults so you don&#x27;t have to.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Out of the box, the helper registers your document, scopes it to endpoints decorated with &lt;code&gt;[MapToApi(documentName)]&lt;/code&gt;, applies Umbraco&#x27;s naming conventions for schemas and operation IDs, and adds the document to the Swagger UI dropdown at &lt;code&gt;/umbraco/openapi/&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&#x26A0;&#xFE0F; &lt;strong&gt;Watch out:&lt;/strong&gt; the first time you call &lt;code&gt;AddBackOfficeOpenApiDocument&lt;/code&gt; (or &lt;code&gt;AddOpenApi&lt;/code&gt; directly) and build, you&#x27;ll hit &lt;code&gt;error CS9137: The &#x27;interceptors&#x27; feature is not enabled in this namespace&lt;/code&gt;. The &lt;code&gt;Microsoft.AspNetCore.OpenApi&lt;/code&gt; source generator (which surfaces your XML doc comments as descriptions in the OpenAPI document, among other compile-time enrichments) propagates through Umbraco&#x27;s project reference, but the property that enables it ships only with the Web SDK. Class-library projects don&#x27;t get it automatically, so add it to your &lt;code&gt;.csproj&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&#xA;&lt;pre class=&quot;highlight xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;&#xA;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;InterceptorsNamespaces&amp;gt;&lt;/span&gt;$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated&lt;span class=&quot;nt&quot;&gt;&amp;lt;/InterceptorsNamespaces&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Minimal usage&#xA;&lt;/h3&gt;&#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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UmbracoExtensionApiComposer&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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-api&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;&lt;p&gt;This will add and configure a &quot;my-api&quot; OpenAPI document that will display the controllers/endpoints that have the attribute &lt;code&gt;[MapToApi(&quot;my-api&quot;)]&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Typical usage&#xA;&lt;/h3&gt;&#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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UmbracoExtensionApiComposer&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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-api&quot;&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;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My API&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;WithBackOfficeAuthentication&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;&lt;p&gt;Besides adding a &quot;my-api&quot; OpenAPI document, this will set the document title to &quot;My API&quot; and declare that the document&#x27;s operations require backoffice authentication (which is what enables the &quot;Authorize&quot; flow in the Swagger UI).&lt;/p&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Builder method reference&#xA;&lt;/h3&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;Method&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;WithTitle(string)&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Sets the document&#x27;s &lt;code&gt;Info.Title&lt;/code&gt;. Also used as the UI dropdown label unless &lt;code&gt;WithUiTitle&lt;/code&gt; is set.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;WithUiTitle(string)&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Overrides only the UI dropdown label, leaving &lt;code&gt;Info.Title&lt;/code&gt; untouched.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;ExcludeFromUi()&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Suppresses registration in the Swagger UI document dropdown. Document is still reachable at its JSON URL.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;WithBackOfficeAuthentication()&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Registers the backoffice OAuth2 security scheme on the document and marks operations as requiring authentication. Swagger UI uses this to prompt for login; generated clients use it to send the token.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;ConfigureOpenApiOptions(Action&amp;lt;OpenApiOptions&amp;gt;)&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Adds a configuration callback for the underlying &lt;code&gt;OpenApiOptions&lt;/code&gt;. Multiple calls will run in order after Umbraco&#x27;s defaults.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;WithJsonOptions(...)&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;Sets the &lt;code&gt;JsonOptions&lt;/code&gt; used when generating the document&#x27;s schema. See Aligning schema serialization below.&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Aligning schema serialization&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Schema generation needs to use the same &lt;code&gt;JsonOptions&lt;/code&gt; your API uses at runtime, otherwise the generated SDKs and the actual payloads will not match. By default, &lt;code&gt;Microsoft.AspNetCore.OpenApi&lt;/code&gt; generates schemas using the global HTTP &lt;code&gt;JsonOptions&lt;/code&gt;, which means whoever hosts your extension can change them and that affects your schema.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For most backoffice extensions you want the BackOffice named &lt;code&gt;JsonOptions&lt;/code&gt; (the same ones Umbraco&#x27;s Management API uses). Point the document at them with &lt;code&gt;WithJsonOptions&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;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;UmbracoExtensionApiComposer&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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-api&quot;&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;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithJsonOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JsonOptionsNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BackOffice&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;For this to be meaningful at runtime, your controllers also need to serialize with those same options. The v17 extension template inherits from a plain &lt;code&gt;ControllerBase&lt;/code&gt;, which uses the default options instead. Fix that by adding &lt;code&gt;[JsonOptionsName(Constants.JsonOptionsNames.BackOffice)]&lt;/code&gt; to your own base controller or to each controller directly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Without it, the schema reflects backoffice serialization but the actual responses are serialized with the default options, and you&#x27;re back to the schema-vs-runtime mismatch.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Going beyond the defaults&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The helper is not a closed box. Anything you&#x27;d normally do on &lt;code&gt;OpenApiOptions&lt;/code&gt; (custom operation/schema/document transformers, your own &lt;code&gt;CreateSchemaReferenceId&lt;/code&gt;, a replacement operation ID transformer, etc.) is still available through &lt;code&gt;ConfigureOpenApiOptions&lt;/code&gt;. It just runs after Umbraco&#x27;s defaults, so you compose on top of them rather than starting from scratch.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For example, registering your own schema transformer:&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;UmbracoExtensionApiComposer&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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-api&quot;&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;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My API&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;ConfigureOpenApiOptions&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;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddSchemaTransformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyExtensionSchemaTransformer&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;h3&gt;&#xA;  &#xA;  &#xA;  Migrating an existing composer&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;If your extension followed the shape of the v17 dotnet template, the migration is: delete the old composer body and write the &lt;code&gt;AddBackOfficeOpenApiDocument(...)&lt;/code&gt; call. Below is the same extension&#x27;s composer before and after.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;v17 (Swashbuckle)&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;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;UmbracoExtensionApiComposer&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;IOperationIdHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CustomOperationHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SwaggerGenOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opt&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;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SwaggerDoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&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;OpenApiInfo&lt;/span&gt;&#xA;            &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;My Test Extension Backoffice API&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;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OperationFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UmbracoExtensionOperationSecurityFilter&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;&#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;UmbracoExtensionOperationSecurityFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BackOfficeSecurityRequirementsOperationFilterBase&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApiName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&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;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomOperationHandler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OperationIdHandler&lt;/span&gt;&#xA;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&#xA;        &lt;span class=&quot;c1&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;v18 (Microsoft OpenAPI &#x2B; new builder)&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;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;UmbracoExtensionApiComposer&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; &lt;span class=&quot;p&quot;&gt;=&amp;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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&#xA;                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithTitle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Test Extension Backoffice API&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;WithBackOfficeAuthentication&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 helper registers &lt;code&gt;UmbracoOperationIdTransformer&lt;/code&gt; by default, so the old &lt;code&gt;IOperationIdHandler&lt;/code&gt; is no longer needed. If you want custom operation ID logic, register your own through &lt;code&gt;ConfigureOpenApiOptions&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;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;UmbracoExtensionApiComposer&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;AddBackOfficeOpenApiDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my-api&quot;&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;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&#xA;            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureOpenApiOptions&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;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddOperationTransformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&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;p&quot;&gt;{&lt;/span&gt;&#xA;                    &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OperationId&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;context&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;n&quot;&gt;ActionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RouteValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;action&quot;&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;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;CompletedTask&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;h2&gt;&#xA;  &#xA;  &#xA;  Use Microsoft&#x27;s AddOpenApi method directly&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;If you want to register a document that isn&#x27;t a backoffice document, or you want to start from a clean slate without Umbraco&#x27;s defaults, call &lt;code&gt;AddOpenApi&lt;/code&gt; yourself. The helper is just a thin wrapper around that method. Umbraco already wires the OpenAPI routing and Swagger UI hosting, so you don&#x27;t need to call &lt;code&gt;MapOpenApi&lt;/code&gt; or configure endpoints. You only need to register your documents.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For the transformer model itself (how operation/schema/document transformers work), see Microsoft&#x27;s &lt;a href=&quot;https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/customize-openapi&quot; rel=&quot;noopener noreferrer&quot;&gt;Customize OpenAPI documents&lt;/a&gt; docs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For Umbraco&#x27;s specifics (&lt;code&gt;[MapToApi]&lt;/code&gt; filtering via &lt;code&gt;options.ShouldInclude&lt;/code&gt;, schema/operation ID conventions, the public &lt;code&gt;UmbracoSchemaIdGenerator.Generate(type)&lt;/code&gt; helper), see the &lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/18.latest/extend-your-project/server-side-extensions/api-versioning-and-openapi&quot; rel=&quot;noopener noreferrer&quot;&gt;API versioning and OpenAPI&lt;/a&gt; docs.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A few things worth remembering:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Backoffice authentication&lt;/strong&gt;: call &lt;code&gt;options.AddBackofficeSecurityRequirements()&lt;/code&gt; (from &lt;code&gt;Umbraco.Cms.Api.Management.OpenApi&lt;/code&gt;) instead of subclassing &lt;code&gt;BackOfficeSecurityRequirementsOperationFilterBase&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Swagger UI registration&lt;/strong&gt;: documents are no longer auto-listed. Call &lt;code&gt;services.AddOpenApiDocumentToUi(documentName, uiTitle)&lt;/code&gt; to surface yours in the dropdown.&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;&lt;code&gt;InterceptorsNamespaces&lt;/code&gt; opt-in&lt;/strong&gt;: same as the helper path. See the Watch out callout at the top of the helper section.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Keep using Swashbuckle&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Not recommended.&lt;/strong&gt; Consider this only if rewriting a complex Swashbuckle setup isn&#x27;t feasible right now. Trade-offs:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Extra dependency on &lt;code&gt;Swashbuckle.AspNetCore&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;OpenAPI 3.0 output, whereas the rest of Umbraco&#x27;s documents emit 3.1.&lt;/li&gt;&#xA;&lt;li&gt;Extra wiring you maintain yourself.&lt;/li&gt;&#xA;&lt;li&gt;Both libraries depend on &lt;code&gt;Microsoft.OpenApi&lt;/code&gt;. A breaking change there that Swashbuckle doesn&#x27;t follow leaves your extension incompatible with the rest of the stack.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;If you still want to go this route, see the &lt;a href=&quot;https://github.com/domaindrivendev/Swashbuckle.AspNetCore#getting-started&quot; rel=&quot;noopener noreferrer&quot;&gt;Swashbuckle.AspNetCore README&lt;/a&gt; for general setup. The rest of this section is the Umbraco-specific wiring.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;1. Install the NuGet package&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Add a &lt;code&gt;PackageReference&lt;/code&gt; to &lt;code&gt;Swashbuckle.AspNetCore&lt;/code&gt; in your extension&#x27;s &lt;code&gt;.csproj&lt;/code&gt; (or &lt;code&gt;dotnet add package Swashbuckle.AspNetCore&lt;/code&gt;).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;2. Register Swashbuckle and your document in a composer&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In v17, Umbraco called &lt;code&gt;AddSwaggerGen&lt;/code&gt; for you and your extension only configured &lt;code&gt;SwaggerGenOptions&lt;/code&gt;. In v18 you have to call it yourself:&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;UmbracoExtensionApiComposer&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;nf&quot;&gt;AddSwaggerGen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opt&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;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SwaggerDoc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&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;OpenApiInfo&lt;/span&gt;&#xA;            &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;My Test Extension Backoffice API&quot;&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;s&quot;&gt;&quot;1.0&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;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previousDocInclusion&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SwaggerGeneratorOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DocInclusionPredicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;            &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;DocInclusionPredicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiDesc&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;docName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&lt;/span&gt;&#xA;                    &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ActionDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;HasMapToApiAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docName&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;previousDocInclusion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiDesc&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;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;// any operation filters, schema filters, or other v17 setup you had...&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;&lt;strong&gt;3. Host the Swagger JSON middleware via an &lt;code&gt;IUmbracoPipelineFilter&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Umbraco no longer calls &lt;code&gt;app.UseSwagger()&lt;/code&gt;, so the middleware that serves the Swagger JSON isn&#x27;t running. Add it through Umbraco&#x27;s pipeline filter, and set the &lt;code&gt;RouteTemplate&lt;/code&gt; to match &lt;code&gt;UmbracoOpenApiOptions.RouteTemplate&lt;/code&gt; (default &lt;code&gt;umbraco/openapi/{documentName}.json&lt;/code&gt;) so the dropdown link resolves.&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;UmbracoExtensionApiComposer&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;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UmbracoPipelineOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;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;nf&quot;&gt;AddFilter&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;UmbracoPipelineFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Swashbuckle&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;PostPipeline&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&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;openApiOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationServices&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;UmbracoOpenApiOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;openApiOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Enabled&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;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;routeTemplate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;openApiOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RouteTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&#xA;                    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseWhen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&#xA;                        &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;routeTemplate&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;s&quot;&gt;&quot;{documentName}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnsureStartsWith&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;n&quot;&gt;branch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSwagger&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;=&amp;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;n&quot;&gt;RouteTemplate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;routeTemplate&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;p&gt;Scope the middleware to your own document&#x27;s URL. Otherwise Swashbuckle intercepts requests for Umbraco&#x27;s own documents (Management, Delivery) and returns 404 because they aren&#x27;t registered with &lt;code&gt;AddSwaggerGen&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;4. Register the document in the Swagger UI dropdown&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Register the document in Umbraco&#x27;s Swagger UI dropdown:&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;UmbracoExtensionApiComposer&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;nf&quot;&gt;AddOpenApiDocumentToUi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Constants&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApiName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My API&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;h2&gt;&#xA;  &#xA;  &#xA;  TL;DR&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The helper is the simplest option for most extensions. &lt;code&gt;AddBackOfficeOpenApiDocument()&lt;/code&gt; is what the v18 template uses, gives you Umbraco&#x27;s sensible defaults out of the box (naming conventions, Swagger UI registration, JsonOptions alignment), and lets you register an OpenAPI document in a single call.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Calling &lt;code&gt;AddOpenApi&lt;/code&gt; yourself is the standard ASP.NET Core approach and works fine. It means more wiring and code to maintain, so reach for it when you need full control (non-backoffice documents, or skipping the Umbraco defaults).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If rewriting really isn&#x27;t an option right now, keeping Swashbuckle as your own dependency and plugging into the dropdown via &lt;code&gt;AddOpenApiDocumentToUi(...)&lt;/code&gt; will get you through, but plan to revisit when you can.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Further reading&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;a href=&quot;https://github.com/umbraco/Announcements/issues/32&quot; rel=&quot;noopener noreferrer&quot;&gt;Announcements #32&lt;/a&gt;: the official announcement, with the full set of changes and reasoning&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/18.latest/extend-your-project/server-side-extensions/custom-backoffice-api&quot; rel=&quot;noopener noreferrer&quot;&gt;Custom Backoffice API&lt;/a&gt;: end-to-end backoffice API setup&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/18.latest/extend-your-project/server-side-extensions/api-versioning-and-openapi&quot; rel=&quot;noopener noreferrer&quot;&gt;API versioning and OpenAPI&lt;/a&gt;: schema IDs, operation IDs, route configuration, endpoint filtering&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;a href=&quot;https://docs.umbraco.com/umbraco-cms/18.latest/get-started/upgrading-and-migrating/version-specific#umbraco-18&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco 18 version-specific upgrade docs&lt;/a&gt;: official migration reference&lt;/li&gt;&#xA;&lt;/ul&gt;]]></description>
                    <pubDate>Tue, 19 May 2026 13:37:37 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.justnik.me/blog/why-i-built-a-passwordless-library-for-umbraco</guid>
                    <title>Building a Passwordless Authentication Library for Umbraco - Part 1: Why?</title>
                    <link>https://www.justnik.me/blog/why-i-built-a-passwordless-library-for-umbraco</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 19 May 2026 12:36:58 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/releasing-image-hotspot-17/</guid>
                    <title>Releasing Image Hotspot 17.0.0</title>
                    <link>https://skttl.dev/blog/releasing-image-hotspot-17/</link>
                    <description><![CDATA[Releasing Image Hotspot 17.0.0 | skttl.dev]]></description>
                    <pubDate>Tue, 19 May 2026 09:23:05 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/ai-on-your-terms-how-umbraco-reimagines-ai/</guid>
                    <title>AI on Your Terms: How Umbraco Reimagines AI</title>
                    <link>https://umbraco.com/blog/ai-on-your-terms-how-umbraco-reimagines-ai/</link>
                    <description><![CDATA[AI is often introduced as a shortcut: more content, more insights, more efficiency. But for many organizations, especially those responsible for digital platforms, the initial excitement is quickly followed by unease. Who is in control of the output? What happens to sensitive data? And how do you ensure that automation doesn&#x2019;t quietly erode quality, tone, or accountability?]]></description>
                    <pubDate>Tue, 19 May 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/uprofile-may-2026-bernadet-goey/</guid>
                    <title>uProfile May 2026 - Bernadet Goey</title>
                    <link>https://umbraco.com/blog/uprofile-may-2026-bernadet-goey/</link>
                    <description><![CDATA[Meet Bernadet: Former PHP-specialist, now a confident Umbraco Developer, looking to give back to the community that supported her journey]]></description>
                    <pubDate>Mon, 18 May 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/hcs.passwordless.otp</guid>
                    <title>HCS.Passwordless.Otp</title>
                    <link>https://marketplace.umbraco.com/package/hcs.passwordless.otp</link>
                    <description><![CDATA[One-time password (OTP) email add-on for Umbraco 17 passwordless member authentication. Delivers a short numeric code by email that the member enters to sign in. No passwords required.]]></description>
                    <pubDate>Mon, 18 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/hcs.passwordless.magiclink</guid>
                    <title>HCS.Passwordless.MagicLink</title>
                    <link>https://marketplace.umbraco.com/package/hcs.passwordless.magiclink</link>
                    <description><![CDATA[Magic link passwordless authentication for Umbraco 17 members. Send a one-click sign-in link by email &#x2014; no passwords required. Includes built-in rate limiting, single-use tokens, and customisable branded emails.]]></description>
                    <pubDate>Mon, 18 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/hcs.passwordless.webauthn</guid>
                    <title>HCS.Passwordless.WebAuthn</title>
                    <link>https://marketplace.umbraco.com/package/hcs.passwordless.webauthn</link>
                    <description><![CDATA[WebAuthn/FIDO2 passkey add-on for Umbraco 17 passwordless member authentication. Enable hardware-backed biometric or security-key sign-in via the browser&#x27;s Credential Management API. No passwords required.]]></description>
                    <pubDate>Mon, 18 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://jcdc.dev/blog/share-the-love-bringing-h5yr-to-jcdcdev</guid>
                    <title>Share the Love - Bringing #H5YR to jcdc.dev</title>
                    <link>https://jcdc.dev/blog/share-the-love-bringing-h5yr-to-jcdcdev</link>
                    <description><![CDATA[I&#x27;ve added the new #H5YR widget to my sites. Send a virtual High Five!]]></description>
                    <pubDate>Sat, 16 May 2026 09:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://uskinned.net/blog/uskinned-site-builder-for-umbraco-17/</guid>
                    <title>uSkinned Site Builder for Umbraco 17</title>
                    <link>https://uskinned.net/blog/uskinned-site-builder-for-umbraco-17/</link>
                    <description><![CDATA[&#x1F4E3; Introducing &#x1F941;&#xA;&#xA;uSkinned Site Builder for Umbraco 17 &#x1F929;&#xA;&#xA;Full support for Umbraco 17 is now available for uSkinned Site Builder. Install or upgrade today.]]></description>
                    <pubDate>Fri, 15 May 2026 14:03:31 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/pulse/from-zero-working-product-two-hours-adam-shallcross-7zeme/</guid>
                    <title>From zero to working product in two hours</title>
                    <link>https://www.linkedin.com/pulse/from-zero-working-product-two-hours-adam-shallcross-7zeme/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 15 May 2026 07:58:10 Z</pubDate>
                        <category>social</category>
                        <category>linkedin</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/modgift.umbraco.breakable</guid>
                    <title>Modgift.Umbraco.Breakable</title>
                    <link>https://marketplace.umbraco.com/package/modgift.umbraco.breakable</link>
                    <description><![CDATA[A Breakable property editor for Umbraco CMS that lets editors mark soft-hyphen break points inside text values using | as a delimiter.]]></description>
                    <pubDate>Fri, 15 May 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.propertytooltip</guid>
                    <title>Our.Umbraco.PropertyToolTip</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.propertytooltip</link>
                    <description><![CDATA[Property ToolTip keeps Umbraco property descriptions available, but shows them as small tooltip help text beside each property label.]]></description>
                    <pubDate>Fri, 15 May 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.ui</guid>
                    <title>Umbraco.AI.Agent.UI</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.ui</link>
                    <description><![CDATA[Shared chat UI components for Umbraco AI Agent surfaces]]></description>
                    <pubDate>Fri, 15 May 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.copilot</guid>
                    <title>Umbraco.AI.Agent.Copilot</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.agent.copilot</link>
                    <description><![CDATA[Copilot chat UI for Umbraco AI Agent]]></description>
                    <pubDate>Fri, 15 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://medium.com/@ben_46907/swiftui-is-a-disaster-9706ffe6016c?source=rss------umbraco-5</guid>
                    <title>SwiftUI is a Disaster</title>
                    <link>https://medium.com/@ben_46907/swiftui-is-a-disaster-9706ffe6016c?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/@ben_46907/swiftui-is-a-disaster-9706ffe6016c?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/800/0*yYaREfb1AnLyqkU5.png&quot; width=&quot;800&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;I&amp;#x2019;ll admit it. I&amp;#x2019;m still programming for Apple platforms using Objective-C, AppKit and UIKit.&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@ben_46907/swiftui-is-a-disaster-9706ffe6016c?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Thu, 14 May 2026 17:24:48 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://joe.gl/ombek/blog/umbicosaurus-deextinction/</guid>
                    <title>The Umbicosaurus De-Extinction: The Community Effort that Brought a Better Icon Picker to Umbraco 17.4</title>
                    <link>https://joe.gl/ombek/blog/umbicosaurus-deextinction/</link>
                    <description><![CDATA[In 2021, I dreamed of improving the icon picker in the Umbraco backoffice and today, in Umbraco 17.4, the package I started is making a splash as part of Core!&#xA;&#xA;This is the story of Umbicosaurus.]]></description>
                    <pubDate>Thu, 14 May 2026 09:58:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.nuget.org/packages/Articulate/6.0.0-rc.2</guid>
                    <title>Articulate v6 RC 2</title>
                    <link>https://www.nuget.org/packages/Articulate/6.0.0-rc.2</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 14 May 2026 08:29:22 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://uskinned.net/blog/umbraco-scotland-meetup-glasgow-edition/</guid>
                    <title>Umbraco Scotland: Glasgow Edition, May 2026</title>
                    <link>https://uskinned.net/blog/umbraco-scotland-meetup-glasgow-edition/</link>
                    <description><![CDATA[Umbraco Scotland: Glasgow Edition, May 2026&#xA;&#xA;A great evening, bringing the Umbraco Scotland community together to share ideas and insights.]]></description>
                    <pubDate>Wed, 13 May 2026 09:00:58 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/doctypedoctor</guid>
                    <title>DocTypeDoctor</title>
                    <link>https://marketplace.umbraco.com/package/doctypedoctor</link>
                    <description><![CDATA[...]]></description>
                    <pubDate>Wed, 13 May 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.smartredirectsuggester</guid>
                    <title>Umbraco.Community.SmartRedirectSuggester</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.smartredirectsuggester</link>
                    <description><![CDATA[Improve the Umbraco trash flow by suggesting redirect targets for published content before it is moved to the recycle bin.]]></description>
                    <pubDate>Wed, 13 May 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.holdyourhorses</guid>
                    <title>Umbraco.Community.HoldYourHorses</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.holdyourhorses</link>
                    <description><![CDATA[Replaces the Save and Publish button in the Umbraco backoffice with a configurable, cancellable countdown &#x2014; gives editors a short window to catch mistakes before content goes live.]]></description>
                    <pubDate>Wed, 13 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://docs.umbraco.com/umbraco-cloud/release-notes/overview-2026/2026-05-releasenotes</guid>
                    <title>May 2026 Cloud Release Notes</title>
                    <link>https://docs.umbraco.com/umbraco-cloud/release-notes/overview-2026/2026-05-releasenotes</link>
                    <description><![CDATA[May 2026 | Cloud | Umbraco Documentation]]></description>
                    <pubDate>Tue, 12 May 2026 15:21:19 Z</pubDate>
                        <category>hq</category>
                </item>
                <item>
                    <guid>https://www.etive-mor.com/blog/carlini-style-vulnerability-hunting-on-a-budget/</guid>
                    <title>Carlini-style software vulnerability hunting, on a budget</title>
                    <link>https://www.etive-mor.com/blog/carlini-style-vulnerability-hunting-on-a-budget/</link>
                    <description><![CDATA[https://www.etive-mor.com/blog/carlini-style-vulnerability-hunting-on-a-budget/Nicholas Carlini did a great talk at [un]prompted a few weeks ago, where he detailed how Anthropic were using LLMs to automate software vulnerability discovery. In this post, I take a look at replicating his process in the Umbraco-CMS repository, for less than 1% the cost.#umbraco #security #llm #ai]]></description>
                    <pubDate>Tue, 12 May 2026 13:31:23 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116561041760397682</guid>
                    <title>The new H5YR.com widget is now LIVE!</title>
                    <link>https://umbracocommunity.social/@owaincodes/116561041760397682</link>
                    <description><![CDATA[The new H5YR.com widget is now LIVE! https://h5yr.com/h5yr-widget/ #umbraco]]></description>
                    <pubDate>Tue, 12 May 2026 13:01:00 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116560322535140460</guid>
                    <title>H5YR.com is now on Umbraco 17</title>
                    <link>https://umbracocommunity.social/@owaincodes/116560322535140460</link>
                    <description><![CDATA[H5YR.com is now on #Umbraco 17 &#x1F389; Thanks @UmbHost for the help with the deployment yesterday. #h5yr]]></description>
                    <pubDate>Tue, 12 May 2026 07:08:15 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/owainwilliams_h5yr-community-umbraco-share-7459538862323924992-iV5D/</guid>
                    <title>H5YR embeddable widget progress</title>
                    <link>https://www.linkedin.com/posts/owainwilliams_h5yr-community-umbraco-share-7459538862323924992-iV5D/</link>
                    <description><![CDATA[H5YR update: https://www.linkedin.com/posts/owainwilliams_h5yr-community-umbraco-share-7459538862323924992-iV5D?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw&#xA;&#xA;Sent from my iPhone]]></description>
                    <pubDate>Mon, 11 May 2026 16:52:10 Z</pubDate>
                        <category>linkedin</category>
                        <category>community</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/designing-an-ai-agent-skill-for-adding-a-page-type-to-umbraco/</guid>
                    <title>Designing an AI Agent skill for adding a page type to Umbraco</title>
                    <link>https://codeshare.co.uk/blog/designing-an-ai-agent-skill-for-adding-a-page-type-to-umbraco/</link>
                    <description><![CDATA[Explore how experienced developers are building AI skills to scaffold Umbraco features. See the conventions and decisions that make page type creation effortless.]]></description>
                    <pubDate>Mon, 11 May 2026 11:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/adamshallcross_quick-update-on-the-ai-search-package-im-ugcPost-7457449420633042944-t2zb</guid>
                    <title>Adam Shallcross AI based search package progress</title>
                    <link>https://www.linkedin.com/posts/adamshallcross_quick-update-on-the-ai-search-package-im-ugcPost-7457449420633042944-t2zb</link>
                    <description><![CDATA[Untitled: https://www.linkedin.com/posts/adamshallcross_quick-update-on-the-ai-search-package-im-ugcPost-7457449420633042944-t2zb?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw&#xA;&#xA;Sent from my iPhone]]></description>
                    <pubDate>Mon, 11 May 2026 06:41:24 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>linkedin</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/diplo.translator</guid>
                    <title>Diplo Translator</title>
                    <link>https://marketplace.umbraco.com/package/diplo.translator</link>
                    <description><![CDATA[Diplo Translator | Umbraco Marketplace]]></description>
                    <pubDate>Fri, 08 May 2026 08:28:41 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.ai.chatbot</guid>
                    <title>Umbraco.Community.AI.Chatbot</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ai.chatbot</link>
                    <description><![CDATA[Drops a public-facing, AI-powered chat widget on a website&#x27;s frontend. End users on the public site type a question; the package runs a semantic search over the site&#x27;s Umbraco content (Umbraco.AI.Search), feeds the matched pages into an Umbraco.AI.Agent, and streams a grounded answer back with cited source pages and follow-up suggestions.&#xA;&#xA;The official Umbraco.AI.* packages expose chat only inside the backoffice. There is no path for an anonymous site visitor to ask questions of the site&#x27;s content. This community package fills that gap.&#xA;&#xA;Key features:&#xA;- Public-facing widget (drop in one &lt;script&gt; tag)&#xA;- Grounded answers with cited source pages&#xA;- Suggested follow-up questions&#xA;- Streaming responses (SSE)&#xA;- Per-IP rate limiting and request size caps&#xA;- Per-instance configuration in the backoffice (welcome message, agent, fallback)&#xA;&#xA;Requires Umbraco CMS 17.3&#x2B;, Umbraco.AI 1.10&#x2B;, Umbraco.AI.Agent 1.9&#x2B;, and a populated Umbraco.AI.Search index.]]></description>
                    <pubDate>Fri, 08 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://dev.to/cultiv/bootstrapping-umbraco-modelsbuilder-on-an-empty-site-age</guid>
                    <title>Bootstrapping Umbraco ModelsBuilder on an empty site</title>
                    <link>https://dev.to/cultiv/bootstrapping-umbraco-modelsbuilder-on-an-empty-site-age</link>
                    <description><![CDATA[&lt;p&gt;The rest of this post was written with Claude AI. Here&#x27;s the simple problem I seem to get myself into often: &lt;/p&gt;&#xA;&#xA;&lt;p&gt;I have views that need a strongly typed model, but modelsbuilder hasn&#x27;t put the models on disk so Umbraco completely fails to start. &lt;code&gt;dotnet run&lt;/code&gt; just fails &#x1F631; But I need to get into the backoffice to generate the models; difficult when Umbraco doesn&#x27;t even run.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;After I encountered this today, I finally took some time to figure out what the easiest way is to get this fixed. What follows is the summary of what Claude did for me:&lt;/p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;Dropped a set of strongly-typed views (&lt;code&gt;home.cshtml&lt;/code&gt;, &lt;code&gt;Author.cshtml&lt;/code&gt;, etc.) into a fresh Umbraco site and ran straight into the chicken-and-egg problem every Umbraco developer eventually meets:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The views reference &lt;code&gt;Umbraco.Cms.Web.Common.PublishedModels.Home&lt;/code&gt; (and friends).&lt;/li&gt;&#xA;&lt;li&gt;Those classes are generated by ModelsBuilder from document types that live in the database.&lt;/li&gt;&#xA;&lt;li&gt;The database is empty until the site has run at least once.&lt;/li&gt;&#xA;&lt;li&gt;The site won&#x27;t build because the views don&#x27;t compile, so it can&#x27;t run.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Here&#x27;s the little dance to break the cycle.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  1. Turn off Razor compile-on-build&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In the &lt;code&gt;.csproj&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 xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;RazorCompileOnBuild&amp;gt;&lt;/span&gt;false&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RazorCompileOnBuild&amp;gt;&lt;/span&gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The build now skips the views entirely instead of failing on the missing &lt;code&gt;PublishedModels.*&lt;/code&gt; types.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  2. Switch ModelsBuilder to manual mode&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In &lt;code&gt;appsettings.json&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 json&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ModelsBuilder&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;ModelsMode&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;SourceCodeManual&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;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;&lt;code&gt;SourceCodeManual&lt;/code&gt; is the only mode that gives you a &lt;strong&gt;Generate models&lt;/strong&gt; button in the backoffice. The other three:&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;Mode&lt;/th&gt;&#xA;&lt;th&gt;Files?&lt;/th&gt;&#xA;&lt;th&gt;Trigger&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;Nothing&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;none&lt;/td&gt;&#xA;&lt;td&gt;&#x2014;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;InMemoryAuto&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;in-memory only&lt;/td&gt;&#xA;&lt;td&gt;auto&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;SourceCodeManual&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;~/umbraco/models/*.cs&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;&lt;strong&gt;button&lt;/strong&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;SourceCodeAuto&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;~/umbraco/models/*.cs&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;auto on schema change&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&lt;/div&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  3. Start the site&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;dotnet run&lt;/code&gt;. The build succeeds (views aren&#x27;t compiled), the app starts, and the backoffice comes up. Frontend pages will 404 or 500 right now &#x2014; that&#x27;s expected and we don&#x27;t care yet.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  4. Click the button&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Backoffice &#x2192; &lt;strong&gt;Settings &#x2192; Models Builder &#x2192; Generate models&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;.cs&lt;/code&gt; files appear in &lt;code&gt;umbraco/models/&lt;/code&gt; &#x2014; one per document type. These are the classes the views were waiting on.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  5. Stop the site&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;Ctrl&#x2B;C&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  6. Re-enable Razor compile-on-build&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Remove the line we added in step 1:&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 xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- &amp;lt;RazorCompileOnBuild&amp;gt;false&amp;lt;/RazorCompileOnBuild&amp;gt;  &#x2190; delete this --&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;  7. Start the site again&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;dotnet run&lt;/code&gt;. Build picks up the generated models, compiles the views against them, the home page renders.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why the dance is needed&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;RazorCompileOnBuild=false&lt;/code&gt; alone isn&#x27;t a real fix &#x2014; it just moves the compile error from build time to the moment a frontend page is hit. Without runtime compilation registered, Umbraco&#x27;s view-engine lookup returns &quot;no physical template file found&quot; and serves a 404. You&#x27;ll see this in the logs:&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;&quot;No physical template file was found for template home&quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&#xA;&#xA;&#xA;&lt;p&gt;The actual fix is to get the models on disk. The flag just buys you the brief window between &quot;build doesn&#x27;t run&quot; and &quot;models exist&quot; so you can boot the backoffice and press the button.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Once the models are committed to source control, none of this matters &#x2014; the next clone builds clean from the start.&lt;/p&gt;]]></description>
                    <pubDate>Thu, 07 May 2026 15:24:58 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://examinex.online/2026/04/16/ai-semantic-ranking-for-azure-search</guid>
                    <title>AI Semantic Ranking with ExamineX and Azure Search</title>
                    <link>https://examinex.online/2026/04/16/ai-semantic-ranking-for-azure-search</link>
                    <description><![CDATA[AI Semantic Ranking with ExamineX and Azure Search]]></description>
                    <pubDate>Thu, 07 May 2026 14:36:09 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://samsolutions.medium.com/umbraco-vs-wordpress-which-cms-platform-is-best-for-your-business-43863d7710c9?source=rss------umbraco-5</guid>
                    <title>Umbraco vs WordPress: Which CMS Platform Is Best for Your Business?</title>
                    <link>https://samsolutions.medium.com/umbraco-vs-wordpress-which-cms-platform-is-best-for-your-business-43863d7710c9?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://samsolutions.medium.com/umbraco-vs-wordpress-which-cms-platform-is-best-for-your-business-43863d7710c9?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1536/1*dxgdjM-1uAleskWDzfMX1Q.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 Content Management System (CMS) is a foundational decision for any digital strategy. The market offers a wide spectrum&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://samsolutions.medium.com/umbraco-vs-wordpress-which-cms-platform-is-best-for-your-business-43863d7710c9?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Thu, 07 May 2026 14:31:51 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/35</guid>
                    <title>[Breaking change]: Remove the built-in Bootstrap 3 horizontal forms theme from Umbraco Forms</title>
                    <link>https://github.com/umbraco/Announcements/issues/35</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 07 May 2026 14:02:35 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/34</guid>
                    <title>[Breaking change]: Replace Pikaday with native HTML date input in the Umbraco Forms default theme</title>
                    <link>https://github.com/umbraco/Announcements/issues/34</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 07 May 2026 14:00:29 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/33</guid>
                    <title>[Breaking change]: Remove jQuery Validation support from Umbraco Forms client-side scripts</title>
                    <link>https://github.com/umbraco/Announcements/issues/33</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 07 May 2026 13:58:03 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/webwonders.umbraco.blockpicker</guid>
                    <title>Webwonders.Umbraco.BlockPicker</title>
                    <link>https://marketplace.umbraco.com/package/webwonders.umbraco.blockpicker</link>
                    <description><![CDATA[A block picker property editor for Umbraco.]]></description>
                    <pubDate>Thu, 07 May 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.mistral</guid>
                    <title>Umbraco.AI.Mistral</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.mistral</link>
                    <description><![CDATA[Mistral provider for Umbraco AI]]></description>
                    <pubDate>Thu, 07 May 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.togetherai</guid>
                    <title>Umbraco.AI.TogetherAI</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.togetherai</link>
                    <description><![CDATA[Together AI provider for Umbraco AI]]></description>
                    <pubDate>Thu, 07 May 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.fireworksai</guid>
                    <title>Umbraco.AI.FireworksAI</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.fireworksai</link>
                    <description><![CDATA[Fireworks AI provider for Umbraco AI]]></description>
                    <pubDate>Thu, 07 May 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.huggingface</guid>
                    <title>Umbraco.AI.HuggingFace</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.huggingface</link>
                    <description><![CDATA[Hugging Face Inference Providers integration for Umbraco AI]]></description>
                    <pubDate>Thu, 07 May 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.deepseek</guid>
                    <title>Umbraco.AI.DeepSeek</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.ai.deepseek</link>
                    <description><![CDATA[DeepSeek provider for Umbraco AI]]></description>
                    <pubDate>Thu, 07 May 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.aivisibility</guid>
                    <title>Umbraco.Community.AiVisibility</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.aivisibility</link>
                    <description><![CDATA[Make your Umbraco site visible to AI search engines (ChatGPT, Claude, Perplexity, Gemini, Bing Copilot) and give editors visibility into who&#x27;s reading the site. Drop-in for Umbraco v17&#x2B; with zero configuration on a typical site.&#xA;&#xA;At install time, every published page becomes reachable as Markdown via /{path}.md or via the standard Accept: text/markdown content negotiation header. Two more public routes ship: /llms.txt (an RFC-style index of every published page) and /llms-full.txt (the concatenated full-Markdown export, hard-capped at a configurable byte limit). All three surfaces are publish-driven cache invalidated and hot-path-protected (If-None-Match &#x2192; 304 &#x2192; single-flight on cache miss).&#xA;&#xA;Two Backoffice dashboards appear under Settings: AI Visibility (site name, summary, per-doctype exclusion, per-page opt-out) and AI Traffic (read-only dashboard showing every AI / human / crawler hit by classification, with date filtering). A Health Check warns when /robots.txt blocks AI crawlers you might not want to block.&#xA;&#xA;Architectural principle: the Umbraco template is the canonical visual form of content. The package renders pages through Umbraco&#x27;s normal Razor pipeline and converts the resulting HTML to Markdown &#x2014; it does NOT walk content properties or template-fork. Whatever your Razor templates produce (Block List, Block Grid, view components, third-party renderers) is what the AI gets, in clean Markdown form.&#xA;&#xA;Six DI extension points let you override extraction, request logging, robots auditing, settings resolution, and UA classification without forking. Apache 2.0 licensed.]]></description>
                    <pubDate>Thu, 07 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://forum.umbraco.com/t/god-mode-v17-finally/7983</guid>
                    <title>God Mode v17 now available</title>
                    <link>https://forum.umbraco.com/t/god-mode-v17-finally/7983</link>
                    <description><![CDATA[God Mode v17 - finally! - Umbraco community forum]]></description>
                    <pubDate>Wed, 06 May 2026 12:37:11 Z</pubDate>
                        <category>community</category>
                        <category>forum</category>
                </item>
                <item>
                    <guid>https://kjac.dev/posts/personalisation-groups-as-segments/</guid>
                    <title>Personalisation Groups as segments</title>
                    <link>https://kjac.dev/posts/personalisation-groups-as-segments/</link>
                    <description><![CDATA[Using the Personalisation Groups package to power segments in Umbraco.]]></description>
                    <pubDate>Wed, 06 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://dev.to/luukpeters/advanced-permissions-for-umbraco-advanced-control-for-complex-scenarios-3fk3</guid>
                    <title>Advanced Permissions for Umbraco: Advanced Control for Complex Scenarios</title>
                    <link>https://dev.to/luukpeters/advanced-permissions-for-umbraco-advanced-control-for-complex-scenarios-3fk3</link>
                    <description><![CDATA[&lt;p&gt;&lt;em&gt;Quick note: I write these blogs myself, AI only redacts them.&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I&#x27;m excited to announce that I just released my very first open source Umbraco community package: &lt;strong&gt;Advanced Permissions for Umbraco&lt;/strong&gt;. In this post, I&#x27;ll explain why I created this package, what it adds to Umbraco&#x27;s permission system, and when you&#x27;d want to use it.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Why This Package Exists&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I&#x27;ve been working with Umbraco since version 4, mostly in an agency context working with enterprise clients. That experience shaped what I value: features that matter at scale. Many of our clients have marketing departments with multiple users and multiple user groups&#x2014;and eventually they reach scenarios where Umbraco&#x27;s built-in permissions don&#x27;t quite fit.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I wrote about these scenarios in a &lt;a href=&quot;https://github.com/umbraco/Umbraco-CMS/discussions/18018&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub discussion&lt;/a&gt; a while back. The current permission system works fine for many use cases. But when your organizational structure gets more complex, you hit some constraints. This package is designed for those situations where you need more granular control.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  The Current Permission System (In a Nutshell)&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;To really understand the benefits of Advanced Permissions, you first need to understand the default Umbraco permission system. Here&#x27;s how it works:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You assign default permissions to a User Group by toggling a list of verbs: &lt;em&gt;Create&lt;/em&gt;, &lt;em&gt;Delete&lt;/em&gt;, &lt;em&gt;Cultures and Hostnames&lt;/em&gt;, &lt;em&gt;Publish&lt;/em&gt;, and so on. Users with that group inherit those permissions.&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%2Ffkbkzzf4dqcsw2307ji8.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%2Ffkbkzzf4dqcsw2307ji8.png&quot; alt=&quot;Umbraco&#x27;s default permission system&quot; width=&quot;800&quot; height=&quot;500&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You can also override these permissions on specific nodes, and those overrides apply to that node and all its descendants. It&#x27;s straightforward and handles most workflows well.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  Where It Reaches Its Limits&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Grant Types and Inheritance&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;In Umbraco, you set default permissions on a User Group, and then you can override those on specific nodes. But here&#x27;s the catch&#x2014;when you override permissions on a node, those node-specific permissions completely replace the defaults. There&#x27;s no inheritance.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So if you want to change just one permission on a node (say, disallow Delete), you have to specify every single permission at that node level. You can&#x27;t say &quot;inherit everything from the default and only change this one thing.&quot; You end up copying all the default permissions to the node level just to tweak one, which is error-prone and a maintenance nightmare. &lt;/p&gt;&#xA;&#xA;&lt;p&gt;And because permissions are just toggles&#x2014;allow (checked) or deny (unchecked)&#x2014;there&#x27;s really only &quot;allow&quot; and a sort of implicit &quot;not-allow.&quot; There&#x27;s no way to explicitly deny something. This matters when you have multiple User Groups on a single user: all their permissions just stack up. You can only gain permissions, never restrict them.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Scope&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Node permissions always apply to a node and its descendants. You can&#x27;t set permissions on just the node itself, or just on descendants.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Take an employee directory: an overview page with employee pages underneath. Ideally, you&#x27;d want editors to leave the overview page alone but be able to delete individual employee pages as they leave. The current system doesn&#x27;t support that directly.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Multiple User Groups&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;When a user has multiple groups, their effective permissions are the combined list of everything allowed across those groups. There&#x27;s no way to say &quot;this group can do X, but that specific group cannot delete content.&quot; You get the union of all their permissions.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  What Advanced Permissions Adds&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The package introduces three capabilities for when you need more control:&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Three Grant Types (Allow, Deny, Inherit)&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Instead of just allow/deny, you get:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Allow&lt;/strong&gt;: explicitly permit something&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Deny&lt;/strong&gt;: explicitly forbid something&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Inherit&lt;/strong&gt;: use whatever the ancestor nodes specifies&lt;/li&gt;&#xA;&lt;/ul&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%2Fxjo7zq8gf19tk6ae9g9b.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%2Fxjo7zq8gf19tk6ae9g9b.png&quot; alt=&quot; &quot; width=&quot;533&quot; height=&quot;480&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This means you only need to set the permissions you want to change and let everything else inherit from above. No more duplicating every permission just to tweak one.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Flexible Scopes&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Choose where permissions apply:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Node &#x2B; Descendants&lt;/strong&gt; (the standard Umbraco behavior)&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Node Only&lt;/strong&gt; (children inherit from ancestors above)&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;strong&gt;Descendants Only&lt;/strong&gt; (the node itself isn&#x27;t affected)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;You can combine node-only and descendants-only permissions on the same node to set different rules for a parent and its children.&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%2Fwe2s1ftb17h24rx2af5n.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%2Fwe2s1ftb17h24rx2af5n.png&quot; alt=&quot; &quot; width=&quot;532&quot; height=&quot;483&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Smart Permission Resolution&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Advanced Permissions includes a resolver that calculates your actual permissions based on priority:&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Explicit deny (takes precedence)&lt;/li&gt;&#xA;&lt;li&gt;Explicit allow&lt;/li&gt;&#xA;&lt;li&gt;Implicit deny&lt;/li&gt;&#xA;&lt;li&gt;Implicit allow&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;It handles multiple User Groups intelligently across all of them.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  Apply Permissions to Everyone&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;There&#x27;s a virtual &quot;All Users&quot; group for permissions that should apply to everyone. Useful for nodes that should never be deleted, for example. It works like any other group from the resolver&#x27;s perspective.&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%2Fkbflq4le9ck1t4blfntd.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%2Fkbflq4le9ck1t4blfntd.png&quot; alt=&quot; &quot; width=&quot;800&quot; height=&quot;178&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;&#xA;  &#xA;  &#xA;  The Access Viewer&#xA;&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;To understand your actual permissions when things get complex, there&#x27;s an Access Viewer that shows your effective permissions for any user or group. Click on any permission and see the resolver chain that determined 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%2F2uymz3li28lo21sqfg15.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%2F2uymz3li28lo21sqfg15.png&quot; alt=&quot; &quot; width=&quot;800&quot; height=&quot;433&quot;&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;&#xA;  &#xA;  &#xA;  When You&#x27;d Use This&#xA;&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You&#x27;d reach for Advanced Permissions when your permission requirements get complex&#x2014;multiple teams with overlapping responsibilities, permissions that need to vary significantly across your content tree, or organizational structures that don&#x27;t fit neatly into the standard model.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For straightforward scenarios, Umbraco&#x27;s built-in permissions work great and you probably don&#x27;t need this. But when you&#x27;re managing access across a large, complex organization, Advanced Permissions gives you the control you need.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You can find the package on the &lt;a href=&quot;https://marketplace.umbraco.com/package/umbraco.community.advancedpermissions&quot; rel=&quot;noopener noreferrer&quot;&gt;Umbraco marketplace&lt;/a&gt;.&lt;/p&gt;]]></description>
                    <pubDate>Tue, 05 May 2026 20:56:44 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://skrift.io/issues/#127</guid>
                    <title>Skrift Issue #127</title>
                    <link>https://skrift.io/issues/#127</link>
                    <description><![CDATA[Featuring guest posts by Dean Leigh on &quot;Document Everything Everywhere All At Once&quot; and Allen Smith on &quot;Deconstructing Umbraco Compose&quot;]]></description>
                    <pubDate>Tue, 05 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                        <category>skrift</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/ai12z.umbraco.widgets</guid>
                    <title>Ai12z.Umbraco.Widgets</title>
                    <link>https://marketplace.umbraco.com/package/ai12z.umbraco.widgets</link>
                    <description><![CDATA[Drag-and-drop ai12z widgets for Umbraco CMS &#x2014; chatbot, CTA search, knowledge box, search results, forms, and containers. Renders native Umbraco Block List elements that emit ai12z web component tags.]]></description>
                    <pubDate>Tue, 05 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/Ambertvu/Umbraco.Community.AdminOnly</guid>
                    <title>Umbraco.Community.AdminOnly</title>
                    <link>https://github.com/Ambertvu/Umbraco.Community.AdminOnly</link>
                    <description><![CDATA[https://github.com/Ambertvu/Umbraco.Community.AdminOnly&#xA;&#xA;Another new package!&#xA;AdminOnly lets administrators hide document types, tabs and properties from everyone who isn&#x27;t an admin &#x2014; without touching user groups, permissions or document type structure. Editors see only what&#x27;s relevant to their work; admins keep the full picture, with a &#x1F512; indicator next to every hidden item.&#xA;&#xA;Heads up &#x2014; this is a backoffice UX layer, not server-side authorization. Hidden items are removed from the UI, but the underlying Umbraco APIs are untouched. A determined user with developer tools can still reach hidden content.]]></description>
                    <pubDate>Mon, 04 May 2026 14:14:48 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/umbraco-cloud-fetch-database-and-media/</guid>
                    <title>Two Umbraco Cloud CLI spinoffs: fetch DB and media into your local dev</title>
                    <link>https://skttl.dev/blog/umbraco-cloud-fetch-database-and-media/</link>
                    <description><![CDATA[Two Umbraco Cloud CLI spinoffs: fetch DB and media into your local dev | skttl.dev]]></description>
                    <pubDate>Mon, 04 May 2026 11:37:40 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/may/a-weekend-without-internet-access/</guid>
                    <title>A weekend without internet access</title>
                    <link>https://owain.codes/blog/2026/may/a-weekend-without-internet-access/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Mon, 04 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/owain.codes/post/3mkwtz5dbwt24</guid>
                    <title>Ideas wanted for h5yr.com</title>
                    <link>https://bsky.app/profile/owain.codes/post/3mkwtz5dbwt24</link>
                    <description><![CDATA[Calling all #Umbraco friends. &#xA;&#xA;Im relaunching h5yr.com and I want all your ideas on what features we can build for the site. &#xA;&#xA;Add your ideas to the issues tracker in github.com/H5YR/High5Yo...&#xA;&#xA;Then together, as a community, we&#x27;ll build them!&#xA;&#xA;]]></description>
                    <pubDate>Sun, 03 May 2026 09:37:39 Z</pubDate>
                        <category>community</category>
                        <category>bluesky</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.enkelmedia.se/blogg/2026/5/1/umbraco-member-thrown-out-after-changing-password?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</guid>
                    <title>Umbraco Member thrown out after changing password</title>
                    <link>https://www.enkelmedia.se/blogg/2026/5/1/umbraco-member-thrown-out-after-changing-password?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=rss</link>
                    <description><![CDATA[&lt;p&gt;I&#x27;m working with a migration of the website for my Umbraco-package &lt;a rel=&quot;noopener&quot; href=&quot;https://www.newsletterstudio.org&quot; target=&quot;_blank&quot;&gt;Newsletter Studio&lt;/a&gt;, from Umbraco 8 to Umbraco 17.&lt;/p&gt;&#xA;&lt;p&gt;I use Umbraco Members to allow customers to login and manage their licenses, update information and download receipts.&lt;/p&gt;&#xA;&lt;h2&gt;New password = Member logged out&lt;/h2&gt;&#xA;&lt;p&gt;When I was working on a feature to allow customers to change password, I noticed that I kept getting thrown out from the logged in area of the website after changing the password.&lt;/p&gt;&#xA;&lt;p&gt;The Member was throw out both:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;When they change password them selfs&lt;/li&gt;&#xA;&lt;li&gt;When the password was changed in from the backoffice&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I searched around for this, and it turns out that this is security feature.&lt;/p&gt;&#xA;&lt;p&gt;Umbraco has introduce settings around concurrent logins both for Users and Members, in appsettings.json one can configure these&lt;/p&gt;&#xA;&lt;div&gt;&#xD;&#xA;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;quot;Umbraco&amp;quot;: {&#xA;    &amp;quot;CMS&amp;quot;: {      &#xA;        &amp;quot;Security&amp;quot;: {&#xA;            &amp;quot;AllowConcurrentLogins&amp;quot;: false&#xA;            &amp;quot;MemberAllowConcurrentLogins&amp;quot;: false&#xA;        }&#xA;    }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xD;&#xA;&lt;/div&gt;&#xD;&#xA;&#xD;&#xA;&#xA;&lt;p&gt;According to the &lt;a rel=&quot;noopener&quot; href=&quot;https://docs.umbraco.com/umbraco-cms/reference/configuration/securitysettings&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;, MemberAllowConcurrentLogins will fallback to the value of AllowConcurrentLogins, which is set to false by default.&lt;/p&gt;&#xA;&lt;h2&gt;What happened?&lt;/h2&gt;&#xA;&lt;p&gt;When the user changed the password, it looked like a new login, since this was not allowed the user was thrown out.&lt;/p&gt;&#xA;&lt;p&gt;There is two ways around this, one could refresh the users login cookie by calling _memberSignInManager.SignInAsync() after the password change.&lt;/p&gt;&#xA;&lt;div&gt;&#xD;&#xA;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var currentMember = await _memberManager.GetCurrentMemberAsync();&#xA;&#xA;var currentPasswordIsValid = await _memberManager.ValidateCredentialsAsync(currentMember.UserName!, model.CurrentPassword!);&#xA;if (!currentPasswordIsValid)&#xA;{&#xA;    return Error();&#xA;}&#xA;&#xA;var changePasswordResult = await _memberManager.ChangePasswordAsync(&#xA;    currentMember,&#xA;    model.CurrentPassword!,&#xA;    model.NewPassword!);&#xA;&#xA;if (!changePasswordResult.Succeeded)&#xA;{&#xA;    return Error();&#xA;}&#xA;&#xA;// Refresh member authentication cookie&#xA;await _memberSignInManager.SignInAsync(currentMember, true, &amp;quot;&amp;quot;);&lt;/code&gt;&lt;/pre&gt;&#xD;&#xA;&lt;/div&gt;&#xD;&#xA;&#xD;&#xA;&#xA;&lt;p&gt;&#xA0;Or, one could set AllowConcurrentLogins to true.&lt;/p&gt;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;&#xA;&lt;p&gt;&#xA0;&lt;/p&gt;]]></description>
                    <pubDate>Fri, 01 May 2026 10:59:59 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/how-i-used-the-new-umbraco-search-on-my-website/</guid>
                    <title>How I used the new Umbraco Search on my website</title>
                    <link>https://codeshare.co.uk/blog/how-i-used-the-new-umbraco-search-on-my-website/</link>
                    <description><![CDATA[Discover how I added powerful search to my blog with category filters and smart sorting. Real-world lessons from building search with Umbraco&amp;#x27;s new platform.]]></description>
                    <pubDate>Fri, 01 May 2026 10:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.advancedpermissions</guid>
                    <title>Umbraco.Community.AdvancedPermissions</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.advancedpermissions</link>
                    <description><![CDATA[Fine-grained permission management for Umbraco that extends the built-in permission system with the controls it lacks: explicit Allow and Deny entries, per-entry scope control (This Node Only, This Node and Descendants, Descendants Only), an All Users Group that applies to every user, and a full reasoning chain that explains why each permission was granted or denied.&#xA;&#xA;Got a critical node that must never be deleted? Set an explicit Deny for Delete on that node for the All Users Group. From that point on, the node cannot be deleted by anyone, from any user group, under any circumstances. An explicit Deny is absolute: nothing overrides it, not an inherited Allow, not an explicit Allow from another user group on the same node. The only way to lift the lock is to remove or edit that one entry. The same pattern works for any permission on any node: lock unpublishing on landing pages, block edits to integration nodes, enforce any other global safety rule.&#xA;&#xA;Adds two menu items to the Users section of the backoffice: a Permissions Editor for managing entries per user group across the content tree, and an Access Viewer for inspecting effective permissions with full reasoning. On first boot the package imports the permissions from your existing user groups automatically, so your current security setup carries over without any manual migration. New user groups created later are seeded the same way.]]></description>
                    <pubDate>Fri, 01 May 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/adamshallcross_so-like-london-buses-it-seems-my-mind-works-ugcPost-7455570629501734912-4wP7?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</guid>
                    <title>Introducing LlmsTxt.Umbraco</title>
                    <link>https://www.linkedin.com/posts/adamshallcross_so-like-london-buses-it-seems-my-mind-works-ugcPost-7455570629501734912-4wP7?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</link>
                    <description><![CDATA[Untitled: https://www.linkedin.com/posts/adamshallcross_so-like-london-buses-it-seems-my-mind-works-ugcPost-7455570629501734912-4wP7?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw&#xA;&#xA;Sent from my iPhone]]></description>
                    <pubDate>Thu, 30 Apr 2026 11:08:48 Z</pubDate>
                        <category>social</category>
                        <category>linkedin</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/32</guid>
                    <title>[Breaking change]: Replace `Swashbuckle.AspNetCore` with `Microsoft.AspNetCore.OpenApi` for Open API document generation</title>
                    <link>https://github.com/umbraco/Announcements/issues/32</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 29 Apr 2026 13:19:40 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/umbraco-cloud-archiver/</guid>
                    <title>I built a CLI to archive Umbraco Cloud projects</title>
                    <link>https://skttl.dev/blog/umbraco-cloud-archiver/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 29 Apr 2026 10:57:01 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.proworks.com/blog/archive/umbraco-ai-page-evaluator/</guid>
                    <title>Catch Weak Content Before It Goes Live with ProWorks Umbraco.AI Page Evaluator</title>
                    <link>https://www.proworks.com/blog/archive/umbraco-ai-page-evaluator/</link>
                    <description><![CDATA[&lt;p&gt;In this day and age, its more important than ever to keep your website content clear, consistent, and on brand. AI infers what your organization does from what it reads on your website and, yes, it reads ALL of your content. Couple this with the rise of AI tools used to outline, write, and enhance marketing content, the risk of publishing AI slop (or even inaccurate information) is high. This is where the &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://github.com/ProWorksCorporation/ProWorks-Umbraco.AI-Page-Evaluator&quot; target=&quot;_blank&quot; title=&quot;ProWorks Umbraco.AI Page Evaluator&quot;&gt;ProWorks Umbraco.AI Page Evaluator&lt;/a&gt; shines.&lt;/p&gt;&#xA;&lt;h2&gt;What Is AI-Powered Page Evaluation?&lt;/h2&gt;&#xA;&lt;p&gt;AI-powered page evaluation uses a large language model (LLM) to analyze your page content and return structured feedback on key page indicators.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A &lt;strong&gt;landing page&lt;/strong&gt; can be evaluated and scored for clarity, messaging strength, objection handling, message hierarchy, and the call to action.&lt;/li&gt;&#xA;&lt;li&gt;A standard &lt;strong&gt;content page&lt;/strong&gt; can be evaluated on SEO performance, AEO/GEO performance, and accessibility compliance.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;All pages&lt;/strong&gt; can be evaluated based on brand guidelines, brand voice, and how it supports the organization&#x27;s marketing strategy.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2&gt;What is ProWorks Umbraco.AI Page Evaluator (AI Page Evaluator)?&lt;/h2&gt;&#xA;&lt;p&gt;Our AI Page Evaluator is an open-source add-on to the &lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://github.com/umbraco/Umbraco.AI/&quot; target=&quot;_blank&quot; title=&quot;Umbraco.AI&quot;&gt;Umbraco.AI&lt;/a&gt; framework, which adds a back-office Evaluate Page button you can simply click on to send the page&#x27;s content to an AI model and return a structured quality report directly inside the back-office, including scored checks, warnings, and actionable suggestions.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/uzfpov4h/screenshot-2026-04-14-162237.png&quot; alt=&quot;Page Evaluator Scoring Report&quot; width=&quot;900&quot; height=&quot;490&quot;&gt;&lt;/p&gt;&#xA;&lt;p&gt;This AI Page Evaluator started because we had a need to evaluate our own Landing Pages and Blog Posts on a rubric to verify content was clear, structured well, and communicated our intent. We had been doing this QA outside of Umbraco and preferred something that lived inside the editorial workflow and was frictionless for content editors to use.&lt;/p&gt;&#xA;&lt;p&gt;ProWorks Umbraco.AI Page Evaluator works with&#xA0;&lt;a rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://umbraco.com/&quot; target=&quot;_blank&quot; title=&quot;Umbraco&quot;&gt;Umbraco&lt;/a&gt; 17.x&#xA0;in combination with the Umbraco.AI official package.&lt;/p&gt;&#xA;&lt;h2&gt;How To Get Started&lt;/h2&gt;&#xA;&lt;p&gt;The install is super-fast, and if you already use Umbraco.AI on Umbraco 17, configuration is fast as well.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Requirements:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Umbraco 17.3&#x2B;&lt;/li&gt;&#xA;&lt;li&gt;Umbraco.AI&lt;/li&gt;&#xA;&lt;li&gt;.Net 10&#x2B;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4&gt;Installation&lt;/h4&gt;&#xA;&lt;p&gt;Use NuGet to install AI Page Evaluator for your Umbraco project.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt; &amp;gt; dotnet add package ProWorks.Umbraco.AI.PageEvaluator&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Or search for &lt;strong&gt;ProWorks.Umbraco.AI.PageEvaluator&lt;/strong&gt; in the NuGet Package Manager in Visual Studio.&lt;/p&gt;&#xA;&lt;p&gt;Package page: &lt;a href=&quot;https://www.nuget.org/packages/ProWorks.Umbraco.AI.PageEvaluator&quot;&gt;https://www.nuget.org/packages/ProWorks.Umbraco.AI.PageEvaluator&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Then build the project, run the website, and login to the back-office.&lt;/p&gt;&#xA;&lt;h4&gt;Configuration&lt;/h4&gt;&#xA;&lt;p&gt;In the back-office, go to the &quot;AI&quot; section. Verify you have a Connection to an AI API and a Profile created that uses the Connection that you want to use.&lt;/p&gt;&#xA;&lt;p&gt;Now go to the Page Evaluator area under Add-ons. Click the &quot;Create New&quot; button and follow the steps below:&lt;br&gt;&lt;br&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/3ibaot4t/screenshot-2026-04-28-165602.png&quot; alt=&quot;Configuration Area&quot; width=&quot;889&quot; height=&quot;528&quot;&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Fill in the &lt;strong&gt;Name &lt;/strong&gt;and &lt;strong&gt;Description&#xA0;&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;Select a &lt;strong&gt;Document Type&lt;/strong&gt; to apply the Page Evaluator to. Simply start typing a DocType name and choose the item from the list.&lt;/li&gt;&#xA;&lt;li&gt;Select the &lt;strong&gt;AI Profile&lt;/strong&gt; to use for this Page Evaluator. Select the &lt;strong&gt;AI Context&lt;/strong&gt; if desired.&lt;br&gt;&lt;br&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/1vabd3fl/screenshot-2026-04-28-165951.png&quot; alt=&quot;Configuration-1&quot; width=&quot;783&quot; height=&quot;578&quot;&gt;&lt;/li&gt;&#xA;&lt;li&gt;Review the &lt;strong&gt;Properties to Evaluate&lt;/strong&gt; and remove any properties that aren&#x27;t important for the type of Page Evaluation you are setting up.&lt;br&gt;&lt;br&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/psddlqul/screenshot-2026-04-28-170238.png&quot; alt=&quot;Properties to Evaluate&quot; width=&quot;666&quot; height=&quot;430&quot;&gt;&lt;/li&gt;&#xA;&lt;li&gt;Paste in the prompt you would like to use or open the &lt;strong&gt;Prompt Builder&lt;/strong&gt; to help guide you on building a prompt.&lt;br&gt;&lt;br&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/tifhcc2q/screenshot-2026-04-28-170336.png&quot; alt=&quot;Screenshot 2026-04-28 170336.png&quot; width=&quot;869&quot; height=&quot;346&quot;&gt;&lt;/li&gt;&#xA;&lt;li&gt;Be sure to &lt;strong&gt;Enable Scoring&lt;/strong&gt; if there are scoring values that will be a part of the prompt.&lt;br&gt;&lt;br&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/fvabbv1k/screenshot-2026-04-28-170829.png&quot; alt=&quot;Enable Scoring&quot; width=&quot;618&quot; height=&quot;110&quot;&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h4&gt;Test it Out&lt;/h4&gt;&#xA;&lt;p&gt;Go to the Content section. Find a page that is of the document type that you specified in the configuration.&#xA0; Press the &lt;strong&gt;Evaluate Page&lt;/strong&gt; button in the bottom right area of the back-office.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/ueggilpw/umbraco-ai-page-evaluation.png&quot; alt=&quot;Umbraco.AI Page Evaluation&quot; width=&quot;542&quot; height=&quot;77&quot;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Page properties will be sent to AI with your evaluation criteria. An evaluation report will display with recommendations, a list of items to review, and scoring (if enabled).&lt;/p&gt;&#xA;&lt;p&gt;The results are cached so that AI doesn&#x27;t have to run each time you want to view the report (save those tokens!). It will re-run the report if the page has been saved since either the last run or if the evaluation criteria has been updated..&lt;/p&gt;&#xA;&lt;p&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; src=&quot;https://www.proworks.com/media/0e5h2u5o/screenshot-2026-04-28-171542.png&quot; alt=&quot;ProWorks Page Evaluation Report&quot; width=&quot;1165&quot; height=&quot;634&quot;&gt;&lt;/p&gt;&#xA;&lt;h2&gt;Key Takeaways&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Content clarity is now a competitive factor and AI systems interpret your pages before many visitors do.&lt;/li&gt;&#xA;&lt;li&gt;ProWorks Umbraco.AI Page Evaluator brings structured, content quality feedback directly into the Umbraco editor.&lt;/li&gt;&#xA;&lt;li&gt;It&#x27;s open source, built by an Umbraco Platinum Partner, and available now on NuGet.&lt;/li&gt;&#xA;&lt;li&gt;Better content evaluation upstream means fewer post-launch surprises.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2&gt;Check it Out!&lt;/h2&gt;&#xA;&lt;p&gt;Try the plugin, open an issue, suggest a feature. It&#x27;s open source and we want to know how teams are using it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a style=&quot;color: #fff; background-image: none;&quot; rel=&quot;noopener&quot; type=&quot;external&quot; href=&quot;https://github.com/ProWorksCorporation/ProWorks-Umbraco.AI-Page-Evaluator&quot; target=&quot;_blank&quot; title=&quot;View the package on Github&quot; class=&quot;pw-button&quot; data-anchor=&quot;#section-footer&quot;&gt;View on Github&lt;/a&gt;&lt;/p&gt;&#xA;&lt;div&gt;&#xA;&lt;h2&gt;Need Help?&lt;/h2&gt;&#xA;&lt;p&gt;If you&#x27;d like help implementing this as part of a broader content quality initiative, or if you&#x27;re evaluating an Umbraco upgrade and want content quality to be part of that conversation, we&#x27;d love to talk.&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;/div&gt;&#xA;&lt;h2&gt;Frequently Asked Questions (FAQ)&lt;/h2&gt;&#xA;&lt;div id=&quot;faq-56aaba21fce947e7a6fc15711e8217cf&quot; class=&quot;accordion&quot;&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-39f1deabb1ac4f2a9990ea5d36668607&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-39f1deabb1ac4f2a9990ea5d36668607&quot;&gt; Does the plugin work with any AI provider, or only Claude / Anthropic? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-39f1deabb1ac4f2a9990ea5d36668607&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;It works with any AI provider configured in Umbraco.AI, including Anthropic Claude, OpenAI, and others. The plugin is provider-agnostic by design; you select the AI profile when setting up an evaluator configuration, so whichever model your team already has access to will work.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-13a35cfe03f24500b5877141b4887768&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-13a35cfe03f24500b5877141b4887768&quot;&gt; What versions of Umbraco does it support? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-13a35cfe03f24500b5877141b4887768&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;The current release requires Umbraco CMS 17.3.x&#x2B;, Umbraco.AI 1.8.x&#x2B;, and .NET 10. If your site is on an earlier version of Umbraco, the plugin is not compatible yet. Support for additional versions may come in future releases.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-ee7fc7bd0f594e0d98f3c22916f27bb4&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-ee7fc7bd0f594e0d98f3c22916f27bb4&quot;&gt; Does the plugin send my content to a third-party API? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-ee7fc7bd0f594e0d98f3c22916f27bb4&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;Yes, it sends page content to whichever AI provider you have configured in Umbraco.AI. Before sending, the plugin cleans the content: HTML tags are stripped and long property values are truncated to reduce token usage. If your organization has data sensitivity requirements, review your AI provider&#x27;s data handling policies before deploying. You can also use the property filtering feature to exclude specific fields from the evaluation entirely.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-eaf3fd5658144d9fbeaefd8e82e38397&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-eaf3fd5658144d9fbeaefd8e82e38397&quot;&gt; Can I control which content gets sent to the AI? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-eaf3fd5658144d9fbeaefd8e82e38397&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;Yes. When setting up an evaluator configuration, you can select exactly which document type properties to include in the evaluation. Any properties you do not select are excluded from the AI call. This is useful both for keeping sensitive fields private and for reducing token usage on pages with large amounts of irrelevant content.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-fd851378877c42f6aef7e6f10c536b3a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-fd851378877c42f6aef7e6f10c536b3a&quot;&gt; Can I customize what the AI evaluates? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-fd851378877c42f6aef7e6f10c536b3a&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;Yes, this is one of the plugin&#x27;s core design principles. Each evaluator configuration uses a custom prompt you write for that specific document type. The Prompt Builder UI can auto-generate a starting prompt from your document type&#x27;s properties and a set of checklist categories, which you can then edit and refine. You can also attach a Context resource from Umbraco.AI to give the model additional background about your organization or brand.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-905523a762f44aca8debd3d2de71521b&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-905523a762f44aca8debd3d2de71521b&quot;&gt; Is the plugin free? Are there any ongoing costs? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-905523a762f44aca8debd3d2de71521b&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;The plugin itself is free and open source under the MIT license. In addition, your AI provider&#x27;s API charges based on token usage. For most sites running on-demand evaluations, API costs are minimal.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&quot;accordion-item&quot;&gt;&#xA;&lt;h4 class=&quot;accordion-header&quot;&gt;&lt;button class=&quot;accordion-button collapsed&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#faq-item-f5a49851eeb34bba9eda56e46284ef6a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;faq-item-f5a49851eeb34bba9eda56e46284ef6a&quot;&gt;&#xA0;What&#x27;s the difference between this plugin and ProWorks&#x27; full AI content evaluation service? &lt;/button&gt;&lt;/h4&gt;&#xA;&lt;div id=&quot;faq-item-f5a49851eeb34bba9eda56e46284ef6a&quot; class=&quot;accordion-collapse collapse&quot;&gt;&#xA;&lt;div class=&quot;accordion-body&quot;&gt;&#xA;&lt;p&gt;The plugin evaluates individual pages on demand inside your Umbraco back office, using prompts you configure. It is a self-serve tool built for ongoing editorial quality control your team can run as part of a regular publishing workflow.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&quot;/ai/&quot; title=&quot;ProWorks&#x27; site-wide AI Content Evaluation&quot;&gt;ProWorks&#x27; site-wide AI Content Evaluation&lt;/a&gt; service is a done-for-you engagement that analyzes how AI systems interpret your entire organization across all public content. It delivers an AI Perspective Report covering your inferred positioning, value propositions, audience signals, and content consistency. This plugin is a great starting point; the service is for teams who want the full picture across their whole site.&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;]]></description>
                    <pubDate>Wed, 29 Apr 2026 10:04:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/april/the-silent-developer/</guid>
                    <title>The Silent Developer</title>
                    <link>https://owain.codes/blog/2026/april/the-silent-developer/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Wed, 29 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/speech-to-text-in-umbraco-ai</guid>
                    <title>Speech-to-Text in Umbraco.AI</title>
                    <link>https://mattbrailsford.dev/speech-to-text-in-umbraco-ai</link>
                    <description><![CDATA[Speech-to-text has landed in Umbraco.AI. Alongside chat and embeddings, transcription is now a first-class capability in the platform &#x2014; with a new...]]></description>
                    <pubDate>Tue, 28 Apr 2026 09:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco</guid>
                    <title>UmbHost.GreenStack.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco</link>
                    <description><![CDATA[A dotnet new template for Umbraco 17 preconfigured for GreenStack hosting with Docker support]]></description>
                    <pubDate>Tue, 28 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.umbootstrap</guid>
                    <title>UmbHost.GreenStack.Umbraco.UmBootstrap</title>
                    <link>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.umbootstrap</link>
                    <description><![CDATA[A dotnet new template for Umbraco 17 with UmBootstrap preconfigured for GreenStack hosting with Docker support]]></description>
                    <pubDate>Tue, 28 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.cleanstarterkit</guid>
                    <title>UmbHost.GreenStack.Umbraco.CleanStarterKit</title>
                    <link>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.cleanstarterkit</link>
                    <description><![CDATA[A dotnet new template for Umbraco 17 with Clean Starter Kit preconfigured for GreenStack hosting with Docker support]]></description>
                    <pubDate>Tue, 28 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umediaops</guid>
                    <title>uMediaOps</title>
                    <link>https://marketplace.umbraco.com/package/umediaops</link>
                    <description><![CDATA[Comprehensive media management suite for Umbraco 17&#x2B; including duplicate detection using SHA256 hashing, unused media finder, backup management, and audit logging.]]></description>
                    <pubDate>Tue, 28 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.thestarterkit</guid>
                    <title>UmbHost.GreenStack.Umbraco.TheStarterKit</title>
                    <link>https://marketplace.umbraco.com/package/umbhost.greenstack.umbraco.thestarterkit</link>
                    <description><![CDATA[A dotnet new template for Umbraco 17 with The Starter Kit preconfigured for GreenStack hosting with Docker support]]></description>
                    <pubDate>Tue, 28 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=V6uPHzSUTeE</guid>
                    <title>All About Migrations: Umbraco Explained</title>
                    <link>https://www.youtube.com/watch?v=V6uPHzSUTeE</link>
                    <description><![CDATA[This recording covers real-world Umbraco migration strategies, including approaches for handling custom data and database changes safely. It also shares practical examples and when to use different migration patterns.&#xA;&#xA;Join one of the world&#x2019;s largest Umbraco communities and be part of the conversation.&#xA;https://www.meetup.com/umbraco-india-user-group/]]></description>
                    <pubDate>Mon, 27 Apr 2026 12:09:22 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/feed/update/urn:li:activity:7453693111752458240/</guid>
                    <title>Merchello v1.0.0 for Umbraco v17&#x2B; is released</title>
                    <link>https://www.linkedin.com/feed/update/urn:li:activity:7453693111752458240/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Sat, 25 Apr 2026 19:19:35 Z</pubDate>
                        <category>social</category>
                        <category>linkedin</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/campaigncanvas</guid>
                    <title>CampaignCanvas</title>
                    <link>https://marketplace.umbraco.com/package/campaigncanvas</link>
                    <description><![CDATA[A Bellissima-native forms and newsletter/campaign platform for Umbraco 17&#x2B; with BlockGrid integration, headless submission API, and commercial licensing.]]></description>
                    <pubDate>Sat, 25 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.ufm.extensions</guid>
                    <title>Umbraco.Community.Ufm.Extensions</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ufm.extensions</link>
                    <description><![CDATA[Extends Umbraco Flavoured Markdown (Ufm) with contentTypeName and contentTypeAlias components, and injects $contentTypeName / $contentTypeAlias scope variables into UfmJS expressions so block labels can fall back to the content type &#x2014; restoring V13-style { title || $contentTypeName } expressions in Umbraco v16 and v17&#x2B;. Supports Umbraco 16 (.NET 9) and Umbraco 17 (.NET 10).]]></description>
                    <pubDate>Sat, 25 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://bsky.app/profile/shazwazza.bsky.social/post/3mjxk4sy4622f</guid>
                    <title>New in ExamineX: AI Semantic Ranking for Umbraco &#x2B; Azure Search</title>
                    <link>https://bsky.app/profile/shazwazza.bsky.social/post/3mjxk4sy4622f</link>
                    <description><![CDATA[&#x1F680; New in #ExamineX: AI Semantic Ranking for #Umbraco &#x2B; Azure Search!&#xA;&#xA;Standard keywords can miss the mark, but semantic ranking uses deep learning to understand the meaning behind queries, re-ranking for true relevance. &#x2728;&#xA;&#xA;&#x2705; Works with Umbraco v13&#x2B;&#xA;&#x1F4D6; Details: bit.ly/4dQldJr&#xA;&#xA;#AzureSearch #AI&#xA;&#xA;]]></description>
                    <pubDate>Fri, 24 Apr 2026 14:50:10 Z</pubDate>
                        <category>community</category>
                        <category>bluesky</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=uY9pZz5NHaQ</guid>
                    <title>umbraCoffee - AI in Umbraco</title>
                    <link>https://www.youtube.com/watch?v=uY9pZz5NHaQ</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;Today we&#x27;re joined by Matt Brailsford - AI Staff Engineer @ Umbraco HQ, Co-founder of CODECABIN and Creator of UMB.FYI.&#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, 24 Apr 2026 09:30:02 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://donutsec.fun/blog/umbackdoor/</guid>
                    <title>UmBackdoor</title>
                    <link>https://donutsec.fun/blog/umbackdoor/</link>
                    <description><![CDATA[New post about a silly old project I was reminded of recently.https://donutsec.fun/blog/umbackdoor/#Umbraco]]></description>
                    <pubDate>Fri, 24 Apr 2026 07:55:50 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/watch?v=PiCBtYWUsLw</guid>
                    <title>The Codegarden Business Track</title>
                    <link>https://www.youtube.com/watch?v=PiCBtYWUsLw</link>
                    <description><![CDATA[The rumors are true &#x1FA84; We&#x2019;ve built a full-blown (magic) Business Track directly into the heart of Codegarden &#x1F3A4;&#xA;&#xA;Why this is a game-changer:&#xA;&#xA;&#x1F4C8; We&#x2019;ve curated a parallel world of ROI, digital transformation, and high-level strategy throughout the two-day conference.&#xA;&#xA;&#x1F977; You can still sneak into the developer talks, but this track means you no longer have to explain to your team why Codegarden is also for you &#x1F609;&#xA;&#xA;&#x1F5D3;&#xFE0F; If your calendar is screaming, you can join us for just one day. It&#x2019;s the perfect one-day deep dive into strategy and networking &#x1F942; It&#x2019;s the magic CEO shortcut.&#xA;&#xA;Will we see you, &#x201C;(p)usiness people&#x2026; business people!&#x201D; in Copenhagen? &#x1F4BC; &#x1F1E9;&#x1F1F0; https://lnkd.in/eCHSzkV]]></description>
                    <pubDate>Thu, 23 Apr 2026 15:44:33 Z</pubDate>
                        <category>hq</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://github.com/umbraco/Announcements/issues/31</guid>
                    <title>[Breaking change]: Umbraco UI Library 2.0 &#x2014; consolidation to a single package</title>
                    <link>https://github.com/umbraco/Announcements/issues/31</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Thu, 23 Apr 2026 09:24:46 Z</pubDate>
                        <category>hq</category>
                        <category>announcement</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.jupix</guid>
                    <title>Our.Umbraco.Jupix</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.jupix</link>
                    <description><![CDATA[Umbraco package that integrates the JUPIX estate agency CRM property feed with Umbraco.&#xA;      Polls the feed on a schedule, caches listings locally, and exposes strongly-typed&#xA;      property models to Razor views, controllers, and headless consumers.]]></description>
                    <pubDate>Thu, 23 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.legalreview</guid>
                    <title>Umbraco.Community.LegalReview</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.legalreview</link>
                    <description><![CDATA[AI-powered legal and regulatory review for Umbraco content. Scans document copy for risky phrases &#x2014; superlatives, health claims, greenwashing, defamation, pricing deception and more &#x2014; fact-checks the underlying claims, and returns a risk score with per-flag suggested fixes. Built on Umbraco.AI and works with any configured provider (OpenAI, Google Gemini, Anthropic, etc.).]]></description>
                    <pubDate>Wed, 22 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.intltelinput</guid>
                    <title>Umbraco.Community.IntlTelInput</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.intltelinput</link>
                    <description><![CDATA[An Umbraco Forms field type that provides an international phone number input with country code dropdown, flags, E.164 validation, and formatting. Powered by intl-tel-input.]]></description>
                    <pubDate>Wed, 22 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=nBEgfifIP0o</guid>
                    <title>DevRel Deep Dive: Umbraco 17.2 &amp; 17.3 highlights with Andy Butland</title>
                    <link>https://www.youtube.com/watch?v=nBEgfifIP0o</link>
                    <description><![CDATA[Sebastiaan and Lotte are joined by the Head of CMS, Andy Butland, to look at the highlights of the two latest Umbraco releases - versions 17.2 and 17.3.&#xA;&#xA;&#x1F440; Umbraco 17.2 release notes: https://releases.umbraco.com/release/umbraco/Umbraco-CMS/17.2.0&#xA;&#x1F440; Umbraco 17.3 release notes: https://releases.umbraco.com/release/umbraco/Umbraco-CMS/17.3.0&#xA;&#xA;&#x1F30D; Matt Brailsford&#x2019;s blogpost on why version 17.4 is going to be &#x201C;a big deal for AI&#x201D;: https://mattbrailsford.dev/why-umbraco-1740-is-a-big-deal-for-ai&#xA;&#xA;00:00 Introduction to the DevRel Deep Dive&#xA;01:09 **Exploring Umbraco Releases 17.2 and 17.3**&#xA;05:04 - Enhancements in User Group Management&#xA;07:14 - Improvements in Content Management Experience&#xA;11:40 - Central Package Management in Umbraco&#xA;16:42 - Optimizing Content Cache Management&#xA;18:03 - Constructive vs Destructive Work&#xA;18:32 - Optimizing API Calls for Performance&#xA;20:00 - Security Updates and Dependency Management&#xA;21:51 - Implementing HMAC for Image Requests&#xA;23:17 - Best Practices for HMAC Keys&#xA;24:36 - Community Contributions and Open Source Practices&#xA;25:53 - Debugging Enhancements in Razor Files&#xA;26:38 **Upcoming Features in Umbraco 17.4**&#xA;27:25 - Website Output Caching Improvements&#xA;29:27 - Enhancements for AI Integration&#xA;31:18 - Lightweight External Members for High Traffic Sites&#xA;&#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>Tue, 21 Apr 2026 10:17:48 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://chromewebstore.google.com/detail/umbraco-debug-tools/ikiladlncgldfhfebghkognepdbnejog</guid>
                    <title>Umbraco Debug Tools</title>
                    <link>https://chromewebstore.google.com/detail/umbraco-debug-tools/ikiladlncgldfhfebghkognepdbnejog</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Tue, 21 Apr 2026 09:29:06 Z</pubDate>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://4e58b28b-864e-4510-9981-431d7c97d58a.azurewebsites.net/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer</guid>
                    <title>Giving Umbraco 17 A Bigger Clock: Raising The SQL Command Timeout With A Composer</title>
                    <link>https://4e58b28b-864e-4510-9981-431d7c97d58a.azurewebsites.net/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer</link>
                    <description><![CDATA[Sometimes you need to override the Command Timeout in code... Here&amp;#x27;s how I did it in Umbraco 17 with a tiny composer. &lt;a href=&quot;https://4e58b28b-864e-4510-9981-431d7c97d58a.azurewebsites.net/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer&quot;&gt;Continue reading&lt;/a&gt;]]></description>
                    <pubDate>Tue, 21 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://aaronsadler.dev/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer</guid>
                    <title>Giving Umbraco 17 A Bigger Clock: Raising The SQL Command Timeout With A Composer</title>
                    <link>https://aaronsadler.dev/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer</link>
                    <description><![CDATA[Sometimes you need to override the Command Timeout in code... Here&amp;#x27;s how I did it in Umbraco 17 with a tiny composer. &lt;a href=&quot;https://aaronsadler.dev/blog/2026/april/21/giving-umbraco-17-a-bigger-clock-raising-the-sql-command-timeout-with-a-composer&quot;&gt;Continue reading&lt;/a&gt;]]></description>
                    <pubDate>Tue, 21 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/what-we-gained-building-umbraco-ai-on-meai-and-maf</guid>
                    <title>What We Gained Building Umbraco AI on Microsoft.Extensions.AI and the Microsoft Agent Framework</title>
                    <link>https://mattbrailsford.dev/what-we-gained-building-umbraco-ai-on-meai-and-maf</link>
                    <description><![CDATA[When we set out to build AI capabilities for Umbraco, we had a choice every platform team faces: build your own abstractions, or bet on someone...]]></description>
                    <pubDate>Tue, 21 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbmediasqueeze</guid>
                    <title>UmbMediaSqueeze</title>
                    <link>https://marketplace.umbraco.com/package/umbmediasqueeze</link>
                    <description><![CDATA[Package Description]]></description>
                    <pubDate>Tue, 21 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://github.com/limbo-works/Limbo.Umbraco.Separator/releases/tag/v17.0.1</guid>
                    <title>Limbo.Umbraco.Separator 17.0.1</title>
                    <link>https://github.com/limbo-works/Limbo.Umbraco.Separator/releases/tag/v17.0.1</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>Tue, 21 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                </item>
                <item>
                    <guid>https://medium.com/@girish.sasikumar.kl33/complete-guide-to-upgrading-umbraco-from-version-13-to-17-f02f0e40237d?source=rss------umbraco-5</guid>
                    <title>Complete Guide: Umbraco Sequence Upgrade from Version 13 to 17</title>
                    <link>https://medium.com/@girish.sasikumar.kl33/complete-guide-to-upgrading-umbraco-from-version-13-to-17-f02f0e40237d?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/complete-guide-to-upgrading-umbraco-from-version-13-to-17-f02f0e40237d?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1408/1*7uHfh_absgEqQrp_4Dn5oA.png&quot; width=&quot;1408&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;For most projects, the best starting point is a direct Umbraco 13 &amp;#x2192; 17 upgrade on a local clone, rather than forcing a 13 &amp;#x2192; 14 &amp;#x2192; 15 &amp;#x2192; 16&amp;#x2026;&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@girish.sasikumar.kl33/complete-guide-to-upgrading-umbraco-from-version-13-to-17-f02f0e40237d?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Mon, 20 Apr 2026 06:54:37 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/our.umbraco.postgresql.umbraco.forms</guid>
                    <title>Our.Umbraco.PostgreSql.Umbraco.Forms</title>
                    <link>https://marketplace.umbraco.com/package/our.umbraco.postgresql.umbraco.forms</link>
                    <description><![CDATA[Adds support for Umbraco Forms with PostgreSQL.]]></description>
                    <pubDate>Mon, 20 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/brevo.umbraco</guid>
                    <title>Brevo.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/brevo.umbraco</link>
                    <description><![CDATA[Umbraco Forms workflow that creates or updates Brevo (Sendinblue) contacts on form submission. Supports field attribute mapping, list assignment, multi-value fields, structured logging, and startup validation.]]></description>
                    <pubDate>Mon, 20 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/mailchimp.umbraco</guid>
                    <title>Mailchimp.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/mailchimp.umbraco</link>
                    <description><![CDATA[Mailchimp workflow integration for Umbraco Forms]]></description>
                    <pubDate>Sat, 18 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/editor-friendly-404-page-in-umbraco/</guid>
                    <title>Editor Friendly 404 Pages in Umbraco</title>
                    <link>https://skttl.dev/blog/editor-friendly-404-page-in-umbraco/</link>
                    <description><![CDATA[Editor Friendly 404 Pages in Umbraco | skttl.dev]]></description>
                    <pubDate>Fri, 17 Apr 2026 14:30:00 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://skttl.dev/blog/auto-register-block-preview-in-umbraco/</guid>
                    <title>Auto-Register Block Previews in Umbraco</title>
                    <link>https://skttl.dev/blog/auto-register-block-preview-in-umbraco/</link>
                    <description><![CDATA[Auto-Register Block Previews in Umbraco | skttl.dev]]></description>
                    <pubDate>Fri, 17 Apr 2026 13:30:59 Z</pubDate>
                        <category>blog</category>
                        <category>community</category>
                </item>
                <item>
                    <guid>https://www.youtube.com/shorts/MHHTFLPqLVc</guid>
                    <title>Fun little photo upload feature for my umbraco site</title>
                    <link>https://www.youtube.com/shorts/MHHTFLPqLVc</link>
                    <description><![CDATA[The Umbraco backoffice isn&#x2019;t great for working with on mobile. I wanted to be able to upload photos I&#x2019;ve taken or even taken previously or even ones I&#x2019;m taking during the upload. Well now I can &#x1F600;&#x1F4F8;]]></description>
                    <pubDate>Fri, 17 Apr 2026 12:30:19 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.bookmarks</guid>
                    <title>Umbraco.Community.Bookmarks</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.bookmarks</link>
                    <description><![CDATA[Bookmark content and media items for easy access and navigation]]></description>
                    <pubDate>Fri, 17 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/ubrokenwindow</guid>
                    <title>uBrokenWindow</title>
                    <link>https://marketplace.umbraco.com/package/ubrokenwindow</link>
                    <description><![CDATA[Generate a new backoffice user account on application startup. Use with care!]]></description>
                    <pubDate>Fri, 17 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://owain.codes/blog/2026/april/ocpowersort-120/</guid>
                    <title>OC.PowerSort 1.2.0</title>
                    <link>https://owain.codes/blog/2026/april/ocpowersort-120/</link>
                    <description><![CDATA[]]></description>
                    <pubDate>Fri, 17 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.schemeweaver</guid>
                    <title>Umbraco.Community.SchemeWeaver</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.schemeweaver</link>
                    <description><![CDATA[Map Umbraco Content Types to any of 780&#x2B; Schema.org types and automatically generate JSON-LD structured data. Features confidence-scored auto-mapping, a backoffice UI for manual fine-tuning, seven property source types (property, static, parent, ancestor, sibling, block content, complex), HTML/URL/date transforms, culture-aware variant support, Delivery API integration, and a Razor tag helper for server-rendered templates. Can also scaffold new Umbraco document types from Schema.org models.]]></description>
                    <pubDate>Fri, 17 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.schemeweaver.usync</guid>
                    <title>Umbraco.Community.SchemeWeaver.uSync</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.schemeweaver.usync</link>
                    <description><![CDATA[uSync addon for SchemeWeaver &#x2014; syncs Schema.org mappings (including resolver config and dynamic root config) between Umbraco environments alongside the doc types they belong to.]]></description>
                    <pubDate>Fri, 17 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=Wrfwi7oGqG8</guid>
                    <title>Using Custom Umbraco AI Tools to Write a Whole Article Automatically</title>
                    <link>https://www.youtube.com/watch?v=Wrfwi7oGqG8</link>
                    <description><![CDATA[I want to be upfront about something, what you&#x2019;re seeing in this video is only possible because of the custom tools I&#x2019;ve written to work with Umbraco AI. Without those, none of this happens.&#xA;&#xA;In this video I show you how I can type a single prompt and have Umbraco AI go off, create a fully structured article, populate it with content blocks, add images, and publish it to my site. All in about 20&#x2013;30 seconds. I find that genuinely exciting and I think you will too.&#xA;&#xA;Here&#x2019;s what I walk through:&#xA;&#xA;- Prompting Umbraco AI to write and publish a complete blog article from scratch&#xA;- Watching it pick and fill content blocks automatically; text, code, tables, quotes, images&#xA;- Asking it to split content and insert a new block mid-article&#xA;- Getting it to add an image block at the end based on a description&#xA;- Seeing the finished article live on the front end&#xA;&#xA;This is the kind of thing that could be a real game changer for your editors, especially if you&#x2019;re on Umbraco 17. Even if you just want a solid starting point for an article rather than a blank page, this sort of tooling makes that really easy.]]></description>
                    <pubDate>Thu, 16 Apr 2026 14:28:29 Z</pubDate>
                        <category>community</category>
                        <category>youtube</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/guardrails-in-umbraco-ai</guid>
                    <title>Guardrails in Umbraco AI</title>
                    <link>https://mattbrailsford.dev/guardrails-in-umbraco-ai</link>
                    <description><![CDATA[When you add AI to a CMS, you&#x2019;re giving it the power to generate content, respond to visitors, and interact with editors. That&#x2019;s powerful &#x2014; but it...]]></description>
                    <pubDate>Thu, 16 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.codeshare.co.uk/blog/using-custom-umbraco-ai-tools-to-write-a-whole-article-automatically/</guid>
                    <title>Using Custom Umbraco AI Tools to Write a Whole Article Automatically</title>
                    <link>https://www.codeshare.co.uk/blog/using-custom-umbraco-ai-tools-to-write-a-whole-article-automatically/</link>
                    <description><![CDATA[In this video I show you how I can type a single prompt and have Umbraco AI go off, create a fully structured article, populate it with content blocks, add images, and publish it to my site.]]></description>
                    <pubDate>Thu, 16 Apr 2026 04:45:49 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/using-custom-umbraco-ai-tools-to-write-a-whole-article-automatically/</guid>
                    <title>Using Custom Umbraco AI Tools to Write a Whole Article Automatically</title>
                    <link>https://codeshare.co.uk/blog/using-custom-umbraco-ai-tools-to-write-a-whole-article-automatically/</link>
                    <description><![CDATA[In this video I show you how I can type a single prompt and have Umbraco AI go off, create a fully structured article, populate it with content blocks, add images, and publish it to my site.]]></description>
                    <pubDate>Thu, 16 Apr 2026 04:45:49 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/agentrun.umbraco</guid>
                    <title>AgentRun.Umbraco</title>
                    <link>https://marketplace.umbraco.com/package/agentrun.umbraco</link>
                    <description><![CDATA[AI-powered workflow engine for Umbraco. Define multi-step agent workflows in YAML and run them from the backoffice. Ships with three ready-to-run examples: Umbraco Content Audit (analyses your site&#x27;s content tree), Content Quality Audit (scores any public URL), and Accessibility Quick-Scan (WCAG 2.1 AA findings). Extensible tool system &#x2014; add custom tools for your editorial workflows. Apache 2.0 licensed, built on Umbraco.AI.]]></description>
                    <pubDate>Thu, 16 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/digitalwonderlab.decluttertypes</guid>
                    <title>DigitalWonderlab.DeclutterTypes</title>
                    <link>https://marketplace.umbraco.com/package/digitalwonderlab.decluttertypes</link>
                    <description><![CDATA[Identify unused document types and data types across your Umbraco instance. Scan your published content and media to find types that are no longer in use, helping you keep your Umbraco installation clean and maintainable.]]></description>
                    <pubDate>Thu, 16 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.ai.browserprovider</guid>
                    <title>Umbraco.Community.AI.BrowserProvider</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.ai.browserprovider</link>
                    <description><![CDATA[Experimental community Browser AI provider for Umbraco AI. Routes AI inference to Chrome&#x27;s built-in Gemini Nano model running locally in the browser. Requires Chrome 127&#x2B; with Prompt API enabled.]]></description>
                    <pubDate>Thu, 16 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbraco.com/blog/the-agent-ready-cms-building-implementation-agent-skills-for-umbraco/</guid>
                    <title>The Agent-Ready CMS: Building Implementation Agent Skills for Umbraco</title>
                    <link>https://umbraco.com/blog/the-agent-ready-cms-building-implementation-agent-skills-for-umbraco/</link>
                    <description><![CDATA[Learn how Implementation Agent Skills for Umbraco will surface &quot;quiet standards&quot; and best practices to help developers build better CMS projects faster and easier.]]></description>
                    <pubDate>Thu, 16 Apr 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.templates.uswds</guid>
                    <title>Umbraco.Community.Templates.USWDS</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.templates.uswds</link>
                    <description><![CDATA[A dotnet new template for creating an Umbraco CMS site with the U.S. Web Design System (USWDS). Includes pre-configured document types, block grid components, Razor views, USWDS Sass pipeline, and optional demo content.]]></description>
                    <pubDate>Thu, 16 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@umbraco/116402714455403613</guid>
                    <title>Partner Summits 2026 annoucned</title>
                    <link>https://umbracocommunity.social/@umbraco/116402714455403613</link>
                    <description><![CDATA[Partner Summits 2026 are here &#x2705;Once again, we&#x2019;re bringing partners together across five markets to give you direct access to HQ insights, product direction, and leadership perspectives.&#x1F1F8;&#x1F1EA; Sweden &#x2013; 23 Sep 2026&#x1F1FA;&#x1F1F8; US &#x2013; Wed 30 Sep &#x2B; Thu 1 Oct 2026&#x1F1EC;&#x1F1E7; UK &#x2013; Thu 15 Oct 2026&#x1F1E9;&#x1F1EA; DACH &#x2013; Wed 11 Nov 2026&#x1F1F3;&#x1F1F1; BeNeLux &#x2013; Wed 18 Nov 2026These are partner-only events, and invitations are sent directly via email &#x1F4E9;If you haven&#x2019;t received yours yet, reach out to your Partner Manager.#Umbraco]]></description>
                    <pubDate>Tue, 14 Apr 2026 11:14:47 Z</pubDate>
                        <category>hq</category>
                        <category>mastodon</category>
                        <category>social</category>
                </item>
                <item>
                    <guid>https://codeshare.co.uk/blog/how-to-bypass-umbraco-cloud-basic-auth-for-preview-urls/</guid>
                    <title>How to Bypass Umbraco Cloud Basic Auth for Preview URLs</title>
                    <link>https://codeshare.co.uk/blog/how-to-bypass-umbraco-cloud-basic-auth-for-preview-urls/</link>
                    <description><![CDATA[Learn how to bypass Umbraco Cloud&amp;#x27;s Basic Authentication for specific preview URLs using the built-in shared secret feature and a lightweight ASP.NET middleware, without exposing the rest of your staging site.]]></description>
                    <pubDate>Tue, 14 Apr 2026 09:55:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.codeshare.co.uk/blog/how-to-bypass-umbraco-cloud-basic-auth-for-preview-urls/</guid>
                    <title>How to Bypass Umbraco Cloud Basic Auth for Preview URLs</title>
                    <link>https://www.codeshare.co.uk/blog/how-to-bypass-umbraco-cloud-basic-auth-for-preview-urls/</link>
                    <description><![CDATA[Learn how to bypass Umbraco Cloud&amp;#x27;s Basic Authentication for specific preview URLs using the built-in shared secret feature and a lightweight ASP.NET middleware, without exposing the rest of your staging site.]]></description>
                    <pubDate>Tue, 14 Apr 2026 09:55:32 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@owaincodes/116401706098179027</guid>
                    <title>Suggest your ideas for a Codegarden T-Shirt design on the community swag store</title>
                    <link>https://umbracocommunity.social/@owaincodes/116401706098179027</link>
                    <description><![CDATA[Planning to make a few new designs for the https://umbracocommunity.store in time for #codegarden this year. I need your help though, what designs / themes would you like to see? Post your ideas below &#x1F447; #Umbraco]]></description>
                    <pubDate>Tue, 14 Apr 2026 09:50:54 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://umbracocommunity.social/@aaronsadleruk/116402331383861205</guid>
                    <title>Dotnet templates released preconfigured for our UmbHost Green Stack platform</title>
                    <link>https://umbracocommunity.social/@aaronsadleruk/116402331383861205</link>
                    <description><![CDATA[We have released 4 dotnet new templates and 4 GitHub template repositories preconfigured for our #GreenStack #Umbraco #hosting platform!2 are empty and clean #Umbraco instances, the others are 3 of the popular starterkit preconfigured (The Starter Kit, Clean Starter Kit &amp; UmBootstrap)This makes it easier than ever to get started!GitHub Template Repositories:https://github.com/UmbHost/GreenStack.Umbracohttps://github.com/UmbHost/GreenStack.Umbraco.CleanStarterKithttps://github.com/UmbHost/GreenStack.Umbraco.TheStarterKithttps://github.com/UmbHost/GreenStack.Umbraco.UmBootstrap]]></description>
                    <pubDate>Tue, 14 Apr 2026 09:50:37 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</category>
                </item>
                <item>
                    <guid>https://mattbrailsford.dev/introducing-umbraco-ai-search</guid>
                    <title>Introducing Umbraco AI Search</title>
                    <link>https://mattbrailsford.dev/introducing-umbraco-ai-search</link>
                    <description><![CDATA[If you&#x2019;ve used the build in search in Umbraco, you&#x2019;ll know it&#x2019;s keyword-based. You type &#x201C;deployment guide&#x201D; and it finds pages containing those...]]></description>
                    <pubDate>Tue, 14 Apr 2026 08:30:00 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://marketplace.umbraco.com/package/umbraco.community.favourites</guid>
                    <title>Umbraco.Community.Favourites</title>
                    <link>https://marketplace.umbraco.com/package/umbraco.community.favourites</link>
                    <description><![CDATA[...]]></description>
                    <pubDate>Tue, 14 Apr 2026 00:00:00 Z</pubDate>
                        <category>community</category>
                        <category>package</category>
                        <category>marketplace</category>
                </item>
                <item>
                    <guid>https://medium.com/@ben_46907/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-de9f68e1fec7?source=rss------umbraco-5</guid>
                    <title>Nah, the Umbraco Community Hasn&#x2019;t Lost Its Spark-We Have</title>
                    <link>https://medium.com/@ben_46907/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-de9f68e1fec7?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/@ben_46907/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-de9f68e1fec7?source=rss------umbraco-5&quot;&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1200/0*10E3ART6rL7yxkkb&quot; width=&quot;1200&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;medium-feed-snippet&quot;&gt;What a contradictory title, please allow me to explain.&lt;/p&gt;&lt;p class=&quot;medium-feed-link&quot;&gt;&lt;a href=&quot;https://medium.com/@ben_46907/nah-the-umbraco-community-hasnt-lost-its-spark-we-have-de9f68e1fec7?source=rss------umbraco-5&quot;&gt;Continue reading on Medium &#xBB;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;]]></description>
                    <pubDate>Mon, 13 Apr 2026 15:56:01 Z</pubDate>
                        <category>community</category>
                        <category>blog</category>
                </item>
                <item>
                    <guid>https://www.linkedin.com/posts/umbukfdn_umbraco-uk-conference-teaser-activity-7447919930936590336-UgQs?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</guid>
                    <title>Umbraco UK Conference announced in London on October 16th 2026</title>
                    <link>https://www.linkedin.com/posts/umbukfdn_umbraco-uk-conference-teaser-activity-7447919930936590336-UgQs?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw</link>
                    <description><![CDATA[https://www.linkedin.com/posts/umbukfdn_umbraco-uk-conference-teaser-activity-7447919930936590336-UgQs?utm_source=share&amp;utm_medium=member_ios&amp;rcm=ACoAAABj9KwBjr581HKBv-M3TbQu69Ia79rmfPw]]></description>
                    <pubDate>Fri, 10 Apr 2026 07:05:13 Z</pubDate>
                        <category>community</category>
                        <category>linkedin</category>
                </item>
                <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://umbraco.com/blog/uprofile-april-2026-will-steed/</guid>
                    <title>uProfile April 2026 - Will Steed</title>
                    <link>https://umbraco.com/blog/uprofile-april-2026-will-steed/</link>
                    <description><![CDATA[Meet Will - (maybe) the youngest Umbraco Certified Master yet?]]></description>
                    <pubDate>Fri, 10 Apr 2026 00:00:00 Z</pubDate>
                        <category>hq</category>
                        <category>blog</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://umbracocommunity.social/@joe/116369670163291835</guid>
                    <title>The value of Open Source Community</title>
                    <link>https://umbracocommunity.social/@joe/116369670163291835</link>
                    <description><![CDATA[At #UmbracoSpark I had the opportunity to chat with Fr&#xE9;d&#xE9;ric Harper about the value of #OpenSourceCommunity, watch the 10min clip here!https://joe.gl/ombek/talks/open-source-communities-giving-back-backstage-with-fred/#Umbraco #OpenSource #Community]]></description>
                    <pubDate>Wed, 08 Apr 2026 15:09:57 Z</pubDate>
                        <category>community</category>
                        <category>social</category>
                        <category>mastodon</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://discord.com/channels/869656431308189746/882981290662580264/1491190165157187724</guid>
                    <title>CSP Manager 17.0.0-beta.1 is out</title>
                    <link>https://discord.com/channels/869656431308189746/882981290662580264/1491190165157187724</link>
                    <description><![CDATA[CSP Manager 17.0.0-beta.1 is out &#x1F389;                                                                                                                                                 &#xA;                                                                                                                                                                                      &#xA;  The major version now aligns with Umbraco CMS &#x2014; so v17 targets Umbraco 17&#x2B;. If you&#x27;re on Umbraco 13&#x2013;16, stay on the 3.x releases.                                                   &#xA;&#xA;  New: Import CSP policies in one click&#xA;  Paste a raw Content-Security-Policy header string, preview the directives, and import everything at once. No more recreating policies by hand.&#xA;&#xA;  New: uSync Publisher support&#xA;  Umbraco.Community.CSPManager.uSync.Complete is a brand new package &#x2014; push and pull CSP policies between Umbraco instances directly from the backoffice using the standard uSync     &#xA;  Publisher workflow.&#xA;&#xA;  The backoffice has been updated for Umbraco 17 with tree navigation and change tracking &#x2014; unsaved changes are detected before you navigate away.&#xA;&#xA;  Under the hood: fully async ICspService, expanded test suite (71 tests, up from 42), and test reports published on every PR.&#xA;&#xA;  Breaking changes to be aware of:&#xA;  - GetOrCreateCspScriptNonce / GetOrCreateCspStyleNonce replaced by a single GetOrCreateCspNonce&#xA;  - CspManagerContext.ScriptNonce / StyleNonce replaced by a single Nonce&#xA;  - Constants.PluginAlias removed &#x2014; use Constants.PackageAlias&#xA;  - ICspService has a new GetCspDefinitionAsync(Guid, CancellationToken) &#x2014; implement it if you have a custom ICspService&#xA;&#xA;  Three packages, one mono repo, all on NuGet:&#xA;  - https://www.nuget.org/packages/Umbraco.Community.CSPManager/17.0.0-beta.1&#xA;  - https://www.nuget.org/packages/Umbraco.Community.CSPManager.uSync/17.0.0-beta.4&#xA;  - https://www.nuget.org/packages/Umbraco.Community.CSPManager.uSync.Complete/]]></description>
                    <pubDate>Wed, 08 Apr 2026 08:06:00 Z</pubDate>
                        <category>social</category>
                        <category>discord</category>
                        <category>community</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/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://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://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/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/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.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://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://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://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.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/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://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://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://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/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://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://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/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://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://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://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://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://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://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://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://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://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://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://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://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://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/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/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://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.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.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.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://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.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.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://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://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.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://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://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/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://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://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/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/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/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.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://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://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>
    </channel>
</rss>