<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kenneth McAndrew</title>
    <description>The latest articles on DEV Community by Kenneth McAndrew (@kmac23va).</description>
    <link>https://dev.to/kmac23va</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F213071%2F252069d4-b03f-4d89-a022-a8b4222bfb3b.jpeg</url>
      <title>DEV Community: Kenneth McAndrew</title>
      <link>https://dev.to/kmac23va</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kmac23va"/>
    <language>en</language>
    <item>
      <title>My Experiences with Sitecore Pathway</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:57:25 +0000</pubDate>
      <link>https://dev.to/kmac23va/my-experiences-with-sitecore-pathway-1258</link>
      <guid>https://dev.to/kmac23va/my-experiences-with-sitecore-pathway-1258</guid>
      <description>&lt;p&gt;I've been on more of a content migration kick lately, and with Sitecore having a product in their world, it was time to crack it open.&lt;/p&gt;

&lt;p&gt;(Just a note, I know it's SitecoreAI now, and I guess SitecoreAI CMS, but XM Cloud just rolls off the tongue...or keyboard...easier. So if you see XMC, it's all the same thing!)&lt;/p&gt;

&lt;p&gt;Pathway was announced at Symposium in 2025, and it started as a "legacy" to XM Cloud concept at the time, all inside the Sitecore ecosystem. That's now expanded to being able to pull content from any site, which is handy, but there are some tradeoffs. So let me walk you through the process from the external side, as I figure that's what most people will be concerned with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Select CMS
&lt;/h2&gt;

&lt;p&gt;This gathers the ingestion data. Pick if this is coming from Sitecore or any other website (we'll assume any other website here). Give the migration setup a name and description, then we get to the big choices. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, for the URLs, you can either put in a sitemap.xml file URL or individual URLs...and here's the catch, you can only go 200 URLs at a time. This seems like a potentially restrictive limit to doing some effective migrations, but I found that when I put in 195 URLs, some of the steps took a good while to run.&lt;/li&gt;
&lt;li&gt;Second, you can select whether you want to import images found on the scraped site. This is on by default and I wouldn't suggest changing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Configure SitecoreAI Instance
&lt;/h2&gt;

&lt;p&gt;Now we start getting into the meat of configuration. Here you'll select your environment and site, and then it asks for the site path. While I would think by default it'd be the Home item of whatever site you picked, I imagine you can pick a subsection if you so choose, which would make sense if you've got more than 200 URLs to go through.&lt;/p&gt;

&lt;p&gt;Then it's going to ask for a a structure file, and I found the instructions a little confusing the way they were worded. Basically, you need to go to Content Editor and go to the site you're working with (whichever one you picked before). Right-click the site node, go to &lt;strong&gt;Scripts&lt;/strong&gt;, then the &lt;strong&gt;Migration&lt;/strong&gt; folder, and then &lt;strong&gt;Download Export Structure&lt;/strong&gt;. The defaults it give you should be all you need, but if you need to add additional templates not in the expected location that XMC/SXA figures, you can add them here. Once you get the JSON file, upload it in this step.&lt;/p&gt;

&lt;p&gt;Once this is all done, hit the button under &lt;strong&gt;Start Web Crawling&lt;/strong&gt; and maybe get a drink, this could take a little bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Content Audit
&lt;/h2&gt;

&lt;p&gt;This section comes in two steps. First is the "grouping" step, which I think looks for commonalities in the URLs for purposes of the next step, which is the first of the two "big boys" in the process.&lt;/p&gt;

&lt;p&gt;For template mapping, you'll find an &lt;strong&gt;AI Instructions&lt;/strong&gt; button that will pop a modal with a bunch of language in there that you can't change. At the bottom, though, is the key - an additional section where you can give the system all the additional instructions you want. For example, if the source system has some metadata that says "this is a landing page" but you have a "landing page" template that wouldn't be the same, designate what default templates should be used. If you have a news article template, you can give instructions on when it should be used (via metadata, URL, etc). This is your chance to be very detailed!&lt;/p&gt;

&lt;p&gt;After you run this mapping, each URL will be listed with the template information, and you can dig in to see what fields in your template will be filled in. At this point, you'll probably find things not to your liking, and then you'd go back, revise your instructions, and go again. This can become a very iterative process, but assuming you have many sites to eventually bring over, you can keep those instructions handy to reuse in the future. One thing I'll note, for example, is the mapper wants to set or unset every available field - this includes the Page Design field, which you might've set via template in standard values. Any presets you might have are likely to get erased, so adding an instruction on fields to maintain would be helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Map Content
&lt;/h2&gt;

&lt;p&gt;The second half of the "big boys" and your last chance to influence things. Another &lt;strong&gt;AI Instructions&lt;/strong&gt; button awaits you, and this time we're talking about components, so more detail would be useful here than even before. Make sure to guide the conversation toward what how you want the system to map what it sees to your components. You might have to dig into CSS classes in the source system, that you can tell the mapper that X class means it should use X component. If you're using a page design with breadcrumbs built in, tell the system not to include the breadcrumbs component you might have. Again, this will be an iterative process, but one you can codify for future use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Migration
&lt;/h2&gt;

&lt;p&gt;This is it, the big one, the one we've all been waiting for. (Hat tip to Harry Potter and Oliver Wood.) It's pretty straightforward here though...hit that &lt;strong&gt;Migration&lt;/strong&gt; button and sit back. You'll get a log of successes and failures, so you'll know if any URLs don't get converted. But now you wait, and then when it's all done, go hit Content Editor and Pages and see what you've wrought.&lt;/p&gt;

&lt;p&gt;That's the good news. The bad news, this doesn't have a redo scenario, where if the final result of generated pages comes up and you're not liking it, you just go back and run it again. No iterative here, the process is done and you'll need to start all the way at the beginning. Make any notes from here to update your AI instructions, and go around again. So I'd start with a subset of the pages you're looking at, some representative samples, so you can drop the bad pages and try again if you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Hard to say, but it appears there's a new version of Pathway coming soon. The &lt;a href="https://symposium.sitecore.com/#agenda" rel="noopener noreferrer"&gt;Sitecore Symposium agenda&lt;/a&gt; lists a session on &lt;strong&gt;Pathway 2.0-the agentic migration: from an application to a toolset&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pathway 2.0 turns migration into a composable toolset with agent-callable capabilities, built-in logging, and step-level traceability. Learn how Sitecore supports Sitecore and non-Sitecore migration paths, and why observable, agentic tooling is a durable foundation for modernization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My guess is there will be more insights into the migration process, to avoid the "bad news" that I mentioned above, and to allow for deeper refinement. This might also help folks do more with the Agentic Toolkit, as I'd wager there might be enhancements there to support this.&lt;/p&gt;

&lt;p&gt;I will say that in the early days, Pathway had a pretty bad reputation, but it does seem to be better now in its external scraping. The 200 URL limit is likely the biggest hurdle to large-scale usage, but given the wait times at some steps, I understand the limitation for now. Hopefully Pathway 2.0 will help there as well.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>pathway</category>
    </item>
    <item>
      <title>Placeholder Maintenance with Sitecore Content SDK</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Fri, 19 Jun 2026 13:59:31 +0000</pubDate>
      <link>https://dev.to/kmac23va/placeholder-maintenance-with-sitecore-content-sdk-20m3</link>
      <guid>https://dev.to/kmac23va/placeholder-maintenance-with-sitecore-content-sdk-20m3</guid>
      <description>&lt;p&gt;During the conversion process from JSS to Content SDK for Sitecore AI (IE XM Cloud), there's some changes to how components are designated and mapped to be aware of, which affect how placeholders work and how those components work inside the placeholders. This led to our needing a solution to handle placeholders in certain circumstances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server vs Client Components
&lt;/h2&gt;

&lt;p&gt;The component designation in Content SDK separates server from client components. This separation is determined by the use of hooks in the code (like &lt;code&gt;useEffect&lt;/code&gt;) and a component is made "client" by placing &lt;code&gt;"use client"&lt;/code&gt; in the first line of the component. Technically you could make every component "client" but you lose out on benefits of server components.&lt;/p&gt;

&lt;p&gt;When it comes to Sitecore, there's a &lt;code&gt;useSitecore&lt;/code&gt; hook that can be used for getting things like the editing state. But you don't need to use that hook, because the new placeholder model, called &lt;code&gt;AppPlaceholder&lt;/code&gt;, that passes the page model down from one level to the next. That includes the edit state, so you can access it in server components as well. This starts all the way up in your &lt;code&gt;Layout.tsx&lt;/code&gt; file, which you'll see in the &lt;a href="https://github.com/Sitecore/xmcloud-starter-js" rel="noopener noreferrer"&gt;starter kit you can download from Github&lt;/a&gt;, and it's a required parameter so it'll always be passed down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You cannot bring server components into a placeholder under a client component.&lt;/strong&gt; &lt;a href="https://doc.sitecore.com/sai/en/developers/content-sdk/20/manage-component-maps-for-client-and-server-components.html#import-component-map-in-client-components" rel="noopener noreferrer"&gt;Sitecore has documentation on this.&lt;/a&gt; It'll work if you set things up manually, but in Pages you won't be allowed to pull components in. To be clear, the scenario is you have a component - let's say a tabs component - that has an &lt;code&gt;AppPlaceholder&lt;/code&gt; on it. The tabs component will typically have some hooks to handle click changes between the tabs, and that requires &lt;code&gt;"use client"&lt;/code&gt; to be designated. Then you'll have a tabs panel component, which you pull in to set up each individual tab, and that also has &lt;code&gt;AppPlaceholder&lt;/code&gt; to hold a variety of other components. Then you might pull in a rich text component, which is likely a server component because it has no hooks. If you just do that straight away, like you would have in JSS, you won't be able to bring those server components in.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Solution
&lt;/h2&gt;

&lt;p&gt;I'll say &lt;strong&gt;a&lt;/strong&gt; solution because I'm sure there's others, and the documentation suggests something, but it wasn't entirely clear to me. What I worked out with some research, AI, and trial and error, was this solution. The solution boils down to this: if you have a component that has &lt;code&gt;AppPlaceholder&lt;/code&gt; and needs to be a client component as well, you have a client/server tag team.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Server Component
&lt;/h3&gt;

&lt;p&gt;The server component part is the actual component you'll be registering in Sitecore as a rendering. It might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppPlaceholder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sitecore-content-sdk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;componentMap&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.sitecore/component-map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TabsClient&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./TabsClient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TabsProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Tabs.types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Tabs — server component wrapper.
 *
 * Owns AppPlaceholder so the Sitecore editor treats the placeholder as
 * server-owned and allows inserting any component type (server or client).
 *
 * Edit mode: renders the placeholder in a single column with a label.
 *
 * Normal mode: passes pre-rendered placeholder content as RSC children to
 * TabsClient, which handles mobile detection and DOM manipulation for the
 * horizontal tab navigation.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Tabs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;TabsProps&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isPageEditing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;isEditing&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`tabs-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;DynamicPlaceholderId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppPlaceholder&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;phKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;rendering&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;componentMap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;componentMap&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPageEditing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;RenderingIdentifier&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`component tabs &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;Styles&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Tabs &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TabsClient&lt;/span&gt;
        &lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rendering&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TabsClient&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that this takes an &lt;code&gt;AppPlaceholder&lt;/code&gt; component and sends it to a component called &lt;code&gt;TabsClient&lt;/code&gt;. That's what we'll look at next, and where all the hooks are working at. But this provides a mechanism by which we continue the "chain" of server components, which allows us to use server or client components in the subsequent placeholder tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Client Component
&lt;/h3&gt;

&lt;p&gt;In our case, the tabs component is designed to switch to an accordion if in mobile mode, so there's a &lt;code&gt;useEffect&lt;/code&gt; and other hooks to handle resize checks. It also takes that &lt;code&gt;children&lt;/code&gt; parameter, which is our &lt;code&gt;AppPlaceholder&lt;/code&gt; fed down from the server component, and now it's safe to use server or client components. You can see all this in the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TabsParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Tabs.types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TabsClientProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TabsParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * TabsClient — handles mobile detection and horizontal tab nav DOM
 * manipulation for Tabs. Receives pre-rendered placeholder content as
 * children from the server component wrapper.
 *
 * After mount, moves .navItem &amp;lt;li&amp;gt; elements from the content area into the
 * nav &amp;lt;ul&amp;gt; and activates the first tab.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TabsClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;TabsClientProps&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;containerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLUListElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsMobile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleResize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setIsMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;576&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;handleResize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleResize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runMove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`.content-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .navItem`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-selected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;panel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s2"&gt;`.content-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; .tab-pane`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;show&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;containerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runMove&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleResize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;handleResize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;component accordions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accordions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;box-shadow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;RenderingIdentifier&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`component tabs &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;Styles&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;
              &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`nav nav-tabs tabs-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;containerRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tablist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`tab-content content-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;renderingUid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This will hopefully provide developers with a starting point for handling placeholders where a client component is required. It should be pretty straightforward to engineer in your handling as appropriate, or to feed the example to AI to help you generate what you need.&lt;/p&gt;

&lt;p&gt;Remember, if your &lt;code&gt;AppPlaceholder&lt;/code&gt; component doesn't require hooks, you don't need to do any of this extra work. Keep it as simple as possible!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>SitecoreAI Scheduled Tasks - Getting PowerShell Working Right</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Tue, 06 Jan 2026 21:06:59 +0000</pubDate>
      <link>https://dev.to/kmac23va/sitecoreai-scheduled-tasks-getting-powershell-working-right-3lg0</link>
      <guid>https://dev.to/kmac23va/sitecoreai-scheduled-tasks-getting-powershell-working-right-3lg0</guid>
      <description>&lt;p&gt;In SitecoreAI CMS (FKA XM Cloud), you have the ability to add PowerShell scripts, which is a great way to enhance functionality and run commands that you might have run in other ways in "legacy" Sitecore. You pretty much have the full run of the content and command structures, though you'll have to dig for it sometimes. A good reference can be found at the &lt;a href="https://sitecorepowershell.com/" rel="noopener noreferrer"&gt;Sitecore PowerShell site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This also allows you to set up PowerShell scripts that can be run as a scheduled task in Sitecore. For example, I have a script that scans recently update taxonomy values, and if anything changed, it finds the related items connected to it and publishes them, so the values are updated in Sitecore Search. But I found that after setting up my script and my task, the task would run but the script wouldn't fire.&lt;/p&gt;

&lt;p&gt;I didn't see this in the documentation directly, but I did find the cause in an AI search about the topic. To get the task to fire the script, the script item has to be in a specific area of the tree structure, but then you'll find that the structure isn't fully in place. To set it up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the /sitecore/system/Modules/PowerShell/Script Library/SPE/Maintenance folder.&lt;/li&gt;
&lt;li&gt;Create a folder called "Scheduled Tasks" - the name is the important thing here.&lt;/li&gt;
&lt;li&gt;Place your script in this folder.&lt;/li&gt;
&lt;li&gt;Set up your scheduled task with the PowerShell Scripted Task Schedule insert option and choose your script.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key is the script location. My enhancement suggestion to Sitecore would be to add the "Scheduled Tasks" folder to their default IAR setup, but you can easily add it to your own. Again, it's the name that matters, not the item template, ID, or the like.&lt;/p&gt;

&lt;p&gt;I hope this helps you out in getting your scheduled tasks going!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>sitecoreai</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Off the Cuff Thoughts About Symposium Day 1 Morning</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Tue, 04 Nov 2025 17:41:47 +0000</pubDate>
      <link>https://dev.to/kmac23va/off-the-cuff-thoughts-about-symposium-day-1-morning-4ad9</link>
      <guid>https://dev.to/kmac23va/off-the-cuff-thoughts-about-symposium-day-1-morning-4ad9</guid>
      <description>&lt;p&gt;So, this is just off the cuff as I sit eating lunch in my hotel room at the Disney Dolphin, where Sitecore Symposium is taking place today and tomorrow (I miss the Thursday half-day already, if only for a good reason to stay for the attendee party). This is my personal opinion on what I heard, and there's three major things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sitecore XM Cloud is now SitecoreAI...and so is the rest of it
&lt;/h3&gt;

&lt;p&gt;So, the first of the two big announcements was that Sitecore XM Cloud was being bundled up with Content Hub, Search, CDP/Personalize, etc, as well as agentic AI, to become SitecoreAI. (Mind you don't put a space in there, Sitecore's official X post called it that...this will become like not capitalizing the C in Sitecore I think.) Apparently licensing will be all based around this all-in-one model, though I'm not sure what this means for entitlements, which were really confusing to navigate and overly restrictive I think. That I'll leave to the bean counters, but the first thought that struck me was a joke I made when Sitecore XM Cloud and that ecosystem was first announced, with the composable stack idea. &lt;/p&gt;

&lt;p&gt;Given they had a product in each main space of the composable stack, I called it the composable monolith. Well, Sitecore even said during the presentations that they're going from composable to composed...so is the monolith back? If so, will there be better connections between products, or can we go back to event handlers over webhooks that had more stability and even a bit more versatility? (Seriously, a webhook for item:deleting...I tried that out, by the time it went to operate on the item being deleted, which you should be able to do, the item was already gone.)&lt;/p&gt;

&lt;p&gt;Time with tell. I do see positives here as it keeps customers in the ecosystem and saves energy in shopping around. But the parts need to work together more smoothly, and the entitlements area needs to be smoothed over.&lt;/p&gt;

&lt;h3&gt;
  
  
  SitecoreAI Pathway was more intriguing to me
&lt;/h3&gt;

&lt;p&gt;Maybe because it's the actual "newer" thing than a marketing deal with AI added in, but Pathway is intended to be a content migration helper from a variety of CMSes, and I imagine can be customized to connect to other ones as well not out of the box. Given how much of a pill content migration can potentially be, this is a product I'd like to find out more about. The confusing part for some was they announced "if you have Sitecore 360, you already have it now" - so is this a service you have access to, is this a professional service that Sitecore will do for you with X number of credits, or what's the scoop? I imagine more will come out in the near future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jesse Cole was the highlight by far
&lt;/h3&gt;

&lt;p&gt;Jesse Cole, the founder of the Savannah Bananas and the whole "banana ball" concept, was excellent. Great energy and messaging. What's funny is, when he said "we're not in the baseball business, we're in the entertainment business," my first thought was Vince McMahon, who said that about WWE and pro wrestling. Jesse doesn't strike me as Vince-like though, thank goodness! I mean, he did have four people come on stage, blindfold themselves, do a dance-off, and the winner was the guy who started taking his shirt off...but it was funny because Jesse made the guy think he was competing still for 30 seconds after the last person was tapped out. And he gave away two pairs of underwear!&lt;/p&gt;

&lt;p&gt;Okay, seriously, dude was funny. And yes he wore the yellow outfit, complete with hat. He needs a monkey mascot called Curious George to finish the ensemble, though. I think Sitecore streamed the morning session, though I'm not sure if this was on there...I think it definitely deserves to be seen by a wider audience. If not, then Jesse is more than welcome to come to my son's tae kwon do school and give them a talk!&lt;/p&gt;

&lt;p&gt;Alright, almost time for the breakouts to start, so if you're reading this and you're at Symposium...you might be late to the first breakout! (It's 12:42pm as I post this.)&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>sitecoresym</category>
    </item>
    <item>
      <title>The Recycle Bin search in XM Cloud doesn't work. Try this instead!</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Mon, 22 Sep 2025 19:59:04 +0000</pubDate>
      <link>https://dev.to/kmac23va/the-recycle-bin-search-in-xm-cloud-doesnt-work-try-this-instead-1m2j</link>
      <guid>https://dev.to/kmac23va/the-recycle-bin-search-in-xm-cloud-doesnt-work-try-this-instead-1m2j</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;EDIT 9/24/2025&lt;/strong&gt;: I had the wrong archival ID in my script, I've fixed that!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had someone ask me to restore an item from the recycle bin in XM Cloud today. Generally not a difficult task, assuming it hasn't been 30 days since the deletion when it should get flushed. But, I've come to find that the recycle bin in XM Cloud, while it has a search, that search...doesn't work. You get a script error.&lt;/p&gt;

&lt;p&gt;Now, in the collection of tools, the recycle bin seems to be a relic of the "old" era - it's in the Desktop of the Content Editor system, and I don't as yet see an equivalent in Explorer or Pages. But it's still a pretty useful tool. Fortunately, there's another way to do this...good old Sitecore PowerShell.&lt;/p&gt;

&lt;p&gt;I remembered from something else I was messing with once that there was a way to get into the recycle bin, so a little documentation and AI searching and crafting, and we get the following number. This script will both search for a term in the name or original path, and if you choose will restore the item(s) back to the master database. A quick little workaround for that pesky search problem!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$searchTerm = ""
$restoreItems = $false

$database = Get-Database -Name "master"
$archive = Get-Archive -Database $database -Name "recyclebin"

# Get all items in the recycle bin
$archiveItems = Get-ArchiveItem -Archive $archive |
    Where-Object {
        $_.Name -match $searchTerm -or
        $_.OriginalLocation -match $searchTerm
    }

foreach ($archivedItem in $archiveItems) {
    Write-Host "Found deleted item: $($archivedItem.Name) | ID: $($archivedItem.ItemId) | Path: $($archivedItem.OriginalLocation)"

    if ($restoreItems) {
        Restore-ArchiveItem -Archive $archive -ItemId $archivedItem.ItemId
        Write-Host "Restored deleted item: $($archivedItem.Name) | ID: $($archivedItem.ItemId) | Path: $($archivedItem.OriginalLocation)"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>sitecore</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Getting fields from a "linked" Sitecore rendering parameter - good news, bad news</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Sat, 20 Sep 2025 10:50:55 +0000</pubDate>
      <link>https://dev.to/kmac23va/need-fields-in-a-linked-rendering-parameter-so-close-2lo4</link>
      <guid>https://dev.to/kmac23va/need-fields-in-a-linked-rendering-parameter-so-close-2lo4</guid>
      <description>&lt;p&gt;Recently working with Sitecore, I had a scenario where I was trying to set a filter key and value to pass to Sitecore Search from XM Cloud via rendering parameter. I have a rendering that gets back search results, but the client wanted to sometimes restrict those by a certain field to a certain value. Since we have all those taxonomy values in Sitecore, we hooked up both key and value as droplink fields. Which makes sense, because you don't want to lose track of the item if it moves.&lt;/p&gt;

&lt;p&gt;Here's the catch...by default, Sitecore Search doesn't capture IDs, but textual values. Well, you can make it capture what you want with the API Push, but you don't want to throw everything in necessarily. Plus by default, you're going to think of getting the textual value in case you want to use it for faceting somewhere else. So you need the name (or display name, or a value in a field) back instead. But by default (a lot of that "default" going around!) you get just the ID in a rendering parameter.&lt;/p&gt;

&lt;p&gt;Enter this Sitecore setting value: &lt;strong&gt;LayoutService.DetailedRenderingParams&lt;/strong&gt;. If you set this to true, then your Edge output will show the custom field output automatically in the spot for the rendering parameters instead of the ID. So there you go, an easy mapping, you get whatever value you need, and you're off to the races!&lt;/p&gt;

&lt;p&gt;Well...close. So close. What if you actually &lt;em&gt;do&lt;/em&gt; need the ID of the item? Or its name or path? Unfortunately, those aren't available in the output. Why I'm not sure, but I had another scenario where I was taking that droplink ID and passing it to an API call for another purpose. If I can't have it both ways, I have to defer to what data I can get (the ID) and get the other data I want another way. In my case, I switched to droplist for the key and text for the value, for now at least. I do have a feature request in for adding these basic values with Sitecore, so hopefully we'll see this become more complete soon.&lt;/p&gt;

&lt;p&gt;An additional reminder, if you turn this feature on, it'll be on for all rendering parameters. You'll want to make sure you review any "link" field types you've used (droplink, treelink, etc etc) for how you're using that parameter value. Even after the ID is added, it won't be as simple as just getting the parameter anymore and having the value, you'll need to map to a model to pop about that ID. So definitely do your regression testing!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Sitecore XM Cloud: The Final(?) Word on Content Resolvers</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Tue, 09 Sep 2025 14:07:42 +0000</pubDate>
      <link>https://dev.to/kmac23va/sitecore-xm-cloud-the-final-word-on-content-resolvers-3k80</link>
      <guid>https://dev.to/kmac23va/sitecore-xm-cloud-the-final-word-on-content-resolvers-3k80</guid>
      <description>&lt;p&gt;This is a follow-up post to my prior two blogs on content resolvers and the issues we've run into with them in play. I felt it was important to share this information with the community, especially as in the days since I wrote the blogs, I actually saw folks posting about using the content resolvers, and I wanted to warn them off.&lt;/p&gt;

&lt;p&gt;First bit of news, we've had some a small testing group testing our alterations on a QA instance, and I'm pleased to report that so far, Edge publishing is working as expected again. To be clear, when you have Edge publishing (AKA v2) running, you should be able to change an item, like a datasource or a subitem to a datasource, and have associated page(s) reflect that change to Edge with only publishing the changed item in question. For example, we have a masthead datasource that's connected to a rendering in a partial view in a shared site, and I made a couple of changes to it and did a smart publish. When I check the Edge Playground for the homepage of my specific site, I see the change reflected. And when I refresh the page after a Netlify revalidation period, I see the change reflected on the front-end as well.&lt;/p&gt;

&lt;p&gt;To be clear, content resolvers do work. But their use has the effect of putting your site into the original "v1" publishing state, where you'll have to republish your site to get things to render out. This was the whole reason Sitecore invented the Edge publishing model, to bring things closer to the "legacy" model that clients came to expect...change and publish something, it appears.&lt;/p&gt;

&lt;p&gt;So this morning, we received this back from Sitecore Support. I wanted to share it, as I believe it justifies the effort we went through (and these blogs), and also it is going to result in documentation changes on their end. I should also give Support a lot of credit, as they diagnosed the issue based on a specific case we provided them among publishing frustrations that we were experiencing...it ended up being the string to pull on to untie the proverbial knot, it seems. So big kudos to them!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After a thorough review of the current architecture, we have to admit that there doesn't seem to be a straightforward way to change the behavior associated with the Navigation Resolver without needing to republish a large number of additional entities. As a result, rendering content resolvers are currently considered incompatible with Edge runtime publishing. We will update the documentation accordingly to reflect this. Thank you for highlighting this point. &lt;/p&gt;

&lt;p&gt;For navigation components, we recommend you consider using either one of the following approaches: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use the "Component Level Data Fetching" approach described in the &lt;a href="https://developers.sitecore.com/learn/accelerate/xm-cloud/implementation/information-architecture/page-listing#component-level-data-fetching" rel="noopener noreferrer"&gt;Performance-optimized page listing&lt;/a&gt; article. This way, you will be able to handle rendering parameters in your component and modify the query to fetch the needed data. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alternatively, you can use integrated GraphQL as mentioned earlier. However, please note that this approach doesn't allow integrating rendering parameters into queries. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;The methodology they describe in item 1 is what &lt;a href="https://dev.to/kmac23va/sitecore-xm-cloud-getting-around-the-navigation-content-resolver-1b5"&gt;my last blog on the navigation resolver&lt;/a&gt; was all about. Fair warning: the out-of-the-box Navigation rendering that ships with Sitecore XM Cloud - or maybe some starter kits in the Content SDK era - uses the navigation content resolver, so if you're thinking of using it, I advise against it.&lt;/p&gt;

&lt;p&gt;I'm glad we were finally able to slough through this for our client, and I really hope this information helps out others in the community. Cheers!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Sitecore XM Cloud: Getting Around the Navigation Content Resolver</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Fri, 05 Sep 2025 01:26:48 +0000</pubDate>
      <link>https://dev.to/kmac23va/sitecore-xm-cloud-getting-around-the-navigation-content-resolver-1b5</link>
      <guid>https://dev.to/kmac23va/sitecore-xm-cloud-getting-around-the-navigation-content-resolver-1b5</guid>
      <description>&lt;p&gt;So, &lt;a href="https://dev.to/kmac23va/rendering-contents-resolvers-and-xm-cloud-just-say-no-1o38"&gt;in my last blog&lt;/a&gt;, I implored you all to avoid rendering contents resolvers. What scares me is I feel like I said it too much, like I called for Beetlejuice, because lately I've seen a couple of blogs discussing their use. I'm very much an advocate against them, but as I noted, the navigation content resolver is a sticky wicket.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2F71u2ir1g4pz4uwd8j3nd.webp" class="article-body-image-wrapper"&gt;&lt;img src="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%2F71u2ir1g4pz4uwd8j3nd.webp" alt="Beetlejuice" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Sticky Wicket?
&lt;/h2&gt;

&lt;p&gt;Well, to be honest, the rendering parameters are the problem. In theory, you could write GraphQL to traverse your site tree, give it a bunch of "children" calls, and call it a day. You might need to up your complexity a lot on the CM side, but the Edge side is pretty hardy. But, those rendering parameters are tricky for a couple of reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The level from/to and navigation filter dropdowns are droplinks, which means you'd need to resolve those IDs first, an extra step. There's a config way to do this, but it's not perfect yet, and I put in a feature request as a result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trickier part...you can't pass rendering parameters to GraphQL on the rendering itself. So you're stuck with GraphQL in your component file, and you need to figure out how to work the levels. Luckily, that's what this blog is to help with!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setup: Rendering Parameters
&lt;/h2&gt;

&lt;p&gt;First things first, make your own rendering parameter template for this effort. I used droplists for the &lt;em&gt;level from&lt;/em&gt;, &lt;em&gt;level to&lt;/em&gt;, and &lt;em&gt;navigation filter&lt;/em&gt; options. I also put in a field for how many children to retrieve, since you have to specify beyond the default of 10. Note that, for now at least, I dropped the &lt;em&gt;start page&lt;/em&gt; parameter (we'll assume we're starting at the homepage) and the &lt;em&gt;flattened&lt;/em&gt; and &lt;em&gt;add root&lt;/em&gt; parameters (we'll assume you'll configure the output as needed). Hook this parameter up to your rendering definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup: The &lt;em&gt;fetchNavData&lt;/em&gt; function
&lt;/h2&gt;

&lt;p&gt;From here, in the TSX file (we're rolling with the OG XM Cloud foundation header), we're wrapping the call for &lt;em&gt;fetchNavData&lt;/em&gt; in a &lt;em&gt;useEffect&lt;/em&gt; call. We're passing in the context ID for the page we're on, the level from, the level to, and the children count.&lt;/p&gt;

&lt;p&gt;First thing we're doing is getting the full Sitecore path of the context ID we're on. Once we've got that, we use the level from value to calculate what the starting path should be, by parsing the Sitecore path down to where it needs to be. Then we build a GraphQL query where we dynamically slide in the number of child levels we need (level to - level from), and finally execute it.&lt;/p&gt;

&lt;p&gt;Now that's a lot to work through, but as I'm a big believer in this, I'm going to provide the code you need. Feel free to tweak it as needed, of course. I'm using this shared code file for both a contextual navigation component and a sitemap component. Note the &lt;em&gt;lib/graphql-client-factory&lt;/em&gt; was in the foundation head I believe, so that code should be findable online.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import graphqlClientFactory from 'lib/graphql-client-factory';

export type NavFilter = {
  fields: {
    Key: {
      value: string;
    };
  };
};

export type NavItem = {
  id: string;
  url: { path: string };
  title: { value: string };
  navTitle?: { value: string };
  navFilter?: { filters: NavFilter[] };
  children?: { results: NavItem[] };
};

export type NavResult = {
  QueryResults: NavItem;
};

export type NavPathResult = {
  QueryResults: { path: string };
};

export type NavSectionProps = {
  key: string;
  item: NavItem;
  navFilter?: string;
};

export const fetchNavData = async (
  contextId: string,
  startLevel: number,
  endLevel: number,
  childrenToShow: number
) =&amp;gt; {
  let navData = null;

  try {
    const graphQLClient = graphqlClientFactory();

    // 1. Get the full path of the context item
    const navPath = await graphQLClient.request&amp;lt;NavPathResult&amp;gt;(NavPathQuery, {
      datasource: contextId,
      language: 'en',
    });

    if (!navPath?.QueryResults?.path) {
      return { navData };
    }

    // 2. Compute the "start path" at the requested level
    const startPath = getPathUpToLevel(navPath.QueryResults.path, startLevel);

    if (!startPath) {
      return { navData };
    }

    // 3. Build the query (just structure, no baked-in path)
    const navQuery = getNavQuery(endLevel - startLevel, childrenToShow);

    // 4. Run query with $datasource = startPath
    navData = await graphQLClient.request&amp;lt;NavResult&amp;gt;(navQuery, {
      datasource: startPath,
      language: 'en',
    });
  } catch (ex) {
    console.error('Failed to load data for nav component:', ex);
  }

  return { navData };
};

function getNavQuery(levels: number, childrenToShow: number): string {
  const fields = `
    id
    url { path }
    title: field(name:"Title") { value }
    navTitle: field(name:"NavigationTitle") { value }
    navFilter: field(name:"NavigationFilter") { filters: jsonValue }
  `;

  const childrenBlock = buildChildren(fields, levels, childrenToShow);

  // Use $datasource instead of hardcoding path
  return `
    query NavDataQuery($datasource: String!, $language: String!) {
      QueryResults: item(path: $datasource, language: $language) {
        ${fields}
        ${childrenBlock}
      }
    }
  `;
}

// Build children nesting (recursive helper)
function buildChildren(fields: string, level: number, first: number): string {
  if (level === 0) {
    return '';
  }

  return `
      children(hasLayout: true, first: ${first}) {
        results {
          ${fields}
          ${buildChildren(fields, level - 1, first)}
        }
      }
    `;
}

function getPathUpToLevel(fullPath: string, level: number): string | null {
  const parts = fullPath.split('/').filter(Boolean);

  // Find "Home" in the Sitecore path
  const homeIndex = parts.indexOf('Home');

  if (homeIndex === -1) {
    return null;
  }

  // Slice from start of path → requested level past Home
  // Example: level=2 =&amp;gt; Home + 1 child
  const endIndex = homeIndex + level;

  if (endIndex &amp;gt; parts.length) {
    return null;
  }

  const wanted = parts.slice(0, endIndex);

  return '/' + wanted.join('/');
}

const NavPathQuery = `
query NavPathQuery($datasource: String!, $language: String!) {
  QueryResults: item(path: $datasource, language: $language) {
    path
  }
}
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage in the Rendering
&lt;/h2&gt;

&lt;p&gt;From there, add the &lt;em&gt;useEffect&lt;/em&gt; call to your rendering TSX file to get the data (included are the relevant references):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const { sitecoreContext } = useSitecoreContext();
  const contextId = sitecoreContext?.route?.itemId ?? '';
  const levelFrom = parseInt(props.params.NavLevelFrom ?? '2');
  const levelTo = parseInt(props.params.NavLevelTo ?? '5');
  const childrenToShow = parseInt(props.params.Children ?? '20');
  const navFilter = props.params.NavFilter ?? '';

  const [navData, setNavData] = useState&amp;lt;NavItem | null&amp;gt;(null);
  const [loading, setLoading] = useState(false);

  // Fetch nav data
  useEffect(() =&amp;gt; {
    if (!contextId) return;

    fetchNavData(contextId, levelFrom, levelTo, childrenToShow)
      .then((data) =&amp;gt; {
        setNavData(data.navData?.QueryResults ?? null);
        setLoading(true);
      })
      .catch((err) =&amp;gt; console.error(err));
  }, [contextId, levelFrom, levelTo, childrenToShow]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data model is in the other code above, so it should be all nice and neat. From there, you can work up the appropriate front-end code to loop through the data model, put in a loading image, or whatever else you'd like to do for the presentation.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Rendering Contents Resolvers and XM Cloud - Just say NO</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Fri, 29 Aug 2025 02:03:10 +0000</pubDate>
      <link>https://dev.to/kmac23va/rendering-contents-resolvers-and-xm-cloud-just-say-no-1o38</link>
      <guid>https://dev.to/kmac23va/rendering-contents-resolvers-and-xm-cloud-just-say-no-1o38</guid>
      <description>&lt;p&gt;There is a feature in Sitecore renderings called Rendering Contents Resolvers...you'll find a dropdown near the bottom of the settings for a rendering. There's a few entries there, like Context Item, Datasource with Children, Context Item with Children, Navigation...all kind of self-explanatory, all useful...&lt;/p&gt;

&lt;p&gt;And all toxic in Sitecore XM Cloud. All. Of. Them.&lt;/p&gt;

&lt;p&gt;Working on a big XM Cloud project, also my first, has been a heck of an experience. One thing early on we struggled with was how to create a sitemap page, and how to create a contextual navigation component. We were directed by Sitecore to the Navigation rendering content resolver, which is also used on their out of the box Navigation rendering. With that "blessing" in mind, we also would use other content resolvers to help expedite some of our work.&lt;/p&gt;

&lt;p&gt;What we ran into was publishing hell. There were times that we'd do a publish from Pages (which is a smart publish by default), only to not see Experience Edge update with the appropriate content. This wasn't just on a site level for that data folder, but even content directly subordinate to the page. It would take a republish, and sometimes more than one, to fix the problem. We're using the Edge publishing model as well, sometimes referred to as the "v2" model, which isn't supposed to require a publish of all pages when a change is made to ancillary data.&lt;/p&gt;

&lt;p&gt;We didn't even thing of the content resolvers, since Sitecore had said the navigation one was okay, so why wouldn't the others? But the struggles on publishing reached a breaking point and we've been raising it so far up the flagpole, I think the flag is popping off the top of the pole. Pinging  support about it when we had a clear sample of the issue, we finally got an answer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The fact that the component uses rendering contents resolver is important because it changes the internal reference to the data source used by the page's layout. Such configuration requires you to republish the page itself to update the layout"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uhhh...what? A content resolver changes the internal reference to the data source used by the page's layout? Well that wasn't advertised! On another matter, Sitecore referred us to their &lt;a href="https://developers.sitecore.com/learn/accelerate/xm-cloud/pre-development/information-architecture/publishing-to-edge" rel="noopener noreferrer"&gt;Publishing to Experience Edge&lt;/a&gt; Accelerate recipe, and sure enough there's a section in there about content resolvers. But...if you read it carefully, it says &lt;strong&gt;custom&lt;/strong&gt; resolvers are not supported, but specifically mentions the navigation resolver that should be utilized.&lt;/p&gt;

&lt;p&gt;Friends, I haven't written a blog in two years, because I was in the PM wilderness, but now that I have crossed back to the garden of development, let me preach to you...&lt;strong&gt;DO NOT USE RENDERING CONTENTS RESOLVERS AT ALL&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fkqfo17w57ud7pbvbxpid.jpg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fkqfo17w57ud7pbvbxpid.jpg" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahem...sorry.&lt;/p&gt;

&lt;p&gt;But seriously...don't. We started yanking them from our project, and I've noticed publishing getting markedly better. The challenging part was the navigation resolver replacement...how to do it right, how to use the rendering parameters, etc...but there was a way. And I'll be glad to share it with you all...&lt;/p&gt;

&lt;p&gt;Next post.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fl4qiyhr2171pa2kmdrjc.gif" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fl4qiyhr2171pa2kmdrjc.gif" alt=" " width="400" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Beyond "local" for Sitecore SXA/XM Cloud</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Sat, 25 Nov 2023 15:28:26 +0000</pubDate>
      <link>https://dev.to/kmac23va/beyond-local-for-sitecore-sxaxm-cloud-j16</link>
      <guid>https://dev.to/kmac23va/beyond-local-for-sitecore-sxaxm-cloud-j16</guid>
      <description>&lt;p&gt;Okay, let's get one thing out of the way. Yes, it's November 25th. Yes, Sitecore MVP applications are due in a few days. Yes, folks write blog posts as a contribution. Yes, it's the last minute. Am I doing this specifically for that purpose?!?&lt;/p&gt;

&lt;p&gt;Not really, but it doesn't hurt! Plus it's harder to put technical blogs together when I'm a project manager now.&lt;/p&gt;

&lt;p&gt;I recently got assigned to my first XM Cloud project...yes, as a project manager, but I'm using what I know of Sitecore and the great community around it to help source information for my team. And it doesn't hurt to keep knowing enough to be dangerous. So as I gather some of these tidbits, I'll try to blog them for our reference and others.&lt;/p&gt;

&lt;p&gt;Today is some research I was doing to set up either partial designs or page branches (a topic I'll go into in another post), and using relative references with them. To clarify, "relative" references in Sitecore are those that use SXA keywords like &lt;code&gt;local&lt;/code&gt; to denote the datasource, like &lt;code&gt;local:/Data/Text 1&lt;/code&gt; when you add the OOTB rich text component to a page. This is different from the traditional Sitecore datasource reference, where you see  a path but behind the scenes is the ID, to keep the content locked in even if you move it. For a relative datasource, someone could move or rename it, and it'll break the datasource. So keep that in mind!&lt;/p&gt;

&lt;p&gt;So if you're an SXA "legacy" user, the &lt;code&gt;local&lt;/code&gt; keyword is one you're familiar with. But there are others in the &lt;a href="https://doc.sitecore.com/xp/en/developers/sxa/103/sitecore-experience-accelerator/use-a-prefix-to-set-the-data-source-context.html" rel="noopener noreferrer"&gt;Sitecore documentation&lt;/a&gt; that we don't usually think of, but could be very useful.&lt;/p&gt;

&lt;p&gt;First is &lt;code&gt;page&lt;/code&gt;, which is actually similar to &lt;code&gt;local&lt;/code&gt;, in that it works relative to the item you're on. The difference comes when applying it from a partial design. On a partial, the &lt;code&gt;local&lt;/code&gt; keyword will always look for the datasource local to the partial design itself, whereas &lt;code&gt;page&lt;/code&gt; always looks for the datasource local to the implementation. So if you had a &lt;code&gt;page&lt;/code&gt; datasource call on your partial design, anywhere you use it needs to have a datasource relative to the page it's used on. For example, if your partial has a hero component pointed to &lt;code&gt;page:Data/Hero&lt;/code&gt; then every page the partial is on needs a &lt;code&gt;Data&lt;/code&gt; folder with an item named &lt;code&gt;Hero&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other I'm finding useful is &lt;code&gt;query&lt;/code&gt; as this lets you take advantage of tokens. This comes in handy if you're storing your datasources in the SXA site's &lt;code&gt;Data&lt;/code&gt; folder (IE that's where you choose to store things, or the datasource is site-wide). SXA adds a &lt;code&gt;$site&lt;/code&gt; token to identify what SXA site you're referring to, which you'll see used a lot in datasource locations in renderings (which sounds like another topic to expound on). But you can use it in your relative datasources too with the &lt;code&gt;query&lt;/code&gt; command, like &lt;code&gt;query:$site/Data/Texts/Text 1&lt;/code&gt; which will look in the site-level Data folder.&lt;/p&gt;

&lt;p&gt;Again, remember that the datasources using these keywords are relative and can break if the item is moved or renamed, a bit more fragile than the typical datasource connection. But there's a solution for that too, if you need it. If you right-click on an item, in the &lt;em&gt;Scripts&lt;/em&gt; menu there is an option for &lt;strong&gt;Convert data sources to GUID&lt;/strong&gt; that you can use to "lock in" those content links. You can also use security to prevent users from renaming or moving items, which would handle the situation as well.&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>xmcloud</category>
      <category>sxa</category>
    </item>
    <item>
      <title>SUGCON NA 2023 - What I'm Looking At</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Sat, 30 Sep 2023 20:29:53 +0000</pubDate>
      <link>https://dev.to/kmac23va/sugcon-na-2023-what-im-looking-at-5b1g</link>
      <guid>https://dev.to/kmac23va/sugcon-na-2023-what-im-looking-at-5b1g</guid>
      <description>&lt;p&gt;It's almost October, but instead of a Sitecore Symposium this year, the event will be a North American SUGCON (I think the first in a number of years). A day and a half in Minneapolis...well a week for me between the MVP Summit, DX, and then SUGCON. So what am I looking at in the agenda? We've got an XM Cloud migration from "legacy" Sitecore on the horizon, so this is a timely event.&lt;/p&gt;

&lt;h1&gt;
  
  
  MVP Summit
&lt;/h1&gt;

&lt;p&gt;Nice try, I can't tell you! :)&lt;/p&gt;

&lt;h1&gt;
  
  
  SUGCON
&lt;/h1&gt;

&lt;p&gt;After the usual opening morning of keynotes and presentations, we get into the breakouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unlocking the Power of AI: Migrating to Sitecore Headless / XM Cloud 10 Times Faster
&lt;/h2&gt;

&lt;p&gt;I'm not sure about the whole AI craze yet (hello Skynet?) but the meat of this session will be about migrating from "legacy" Sitecore to the headless/XM Cloud Sitecore. This is a trend that's going to ramp up more as time goes on, I think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crafting lightning-fast composable digital experiences
&lt;/h2&gt;

&lt;p&gt;One of the big draws of the headless/composable architecture, besides the composable, is the performance gains that are touted on the end sites, from load speed to Lighthouse scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Hub DAM Maturity Model
&lt;/h2&gt;

&lt;p&gt;Content Hub is another of those areas to be looked at more closely...let's face it, while the media library has been convenient, it's not designed for ultra-rich amounts of content. We have a client that is probably near 200 GB of media content in the media library, but it's only by the grace of Azure blob storage that we aren't going mad. This is the next step up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build for XM Cloud according to the Architect of XM Cloud
&lt;/h2&gt;

&lt;p&gt;Let's face it, if you could learn at the feet of the creator, wouldn't you?&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Sitecore Search
&lt;/h2&gt;

&lt;p&gt;Without Solr in the mix for the headless front-end, search solutions will become a bigger lift; for example, no Coveo out-of-box components this time if you go that way. Sitecore introduced its own search platform that will likely be looked at very closely. After all, while it's a composable stack, staying "in the family" likely has benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  A University's Journey to the Creation of their Composable Roadmap
&lt;/h2&gt;

&lt;p&gt;I'll be honest, my original intention was to go to "Tips and tricks for Next.js and Sitecore Headless" but I'm co-presenting this session, so I'm occupied! We're going to talk about our roadmap project with Michigan State University and their march to composable. Come on by before dinner!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building No Code Integrations with Sitecore Webhooks and GraphQL
&lt;/h2&gt;

&lt;p&gt;Webhooks are the next evolution from the old pipelines we used to write, so I'm curious to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Compromises in XMC: On Demand Revalidation with Experience Edge
&lt;/h2&gt;

&lt;p&gt;This is a session about speeding up getting your content from author to end-user, something that is always handy.&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>sugcon</category>
      <category>xmcloud</category>
    </item>
    <item>
      <title>Sitecore Symposium Aftermath: My Thoughts on XM Cloud</title>
      <dc:creator>Kenneth McAndrew</dc:creator>
      <pubDate>Mon, 24 Oct 2022 01:54:33 +0000</pubDate>
      <link>https://dev.to/kmac23va/sitecore-symposium-aftermath-my-thoughts-on-xm-cloud-250j</link>
      <guid>https://dev.to/kmac23va/sitecore-symposium-aftermath-my-thoughts-on-xm-cloud-250j</guid>
      <description>&lt;p&gt;So I got home yesterday from Sitecore Symposium and it was an interesting time. My feet are finally recovered from all the walking around the Museum of Science, at least! But you didn't come here for that. I have some thoughts on stuff I heard at the conference that I wanted to share. I should note, these are my opinions, and just because I'm a Sitecore MVP doesn't mean I have to cheerlead everything they do. They're getting there with this stuff, but there's work to be done for sure.&lt;/p&gt;

&lt;p&gt;So, XM Cloud. Sitecore announced its availability in July, when it wasn't really (marketing and development really need to talk more). A few partners have gotten in there to look around, and they've written some blogs about it that you can find out there. This isn't looking at all of the technical details, but more the overarching concept and its readiness for prime time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Good
&lt;/h2&gt;

&lt;p&gt;XM Cloud is Sitecore's foray into the SaaS space, an evolution of the managed cloud approach they're using now. The big difference is, Sitecore will handle the updates and upgrades for you. Given how much of a pain these can be sometimes, that'll be a welcome note for many companies. In the background, XM Cloud is run on containers, making this an easy prospect. (A hope on my part is that Sitecore will start patching their existing 10.x containers with hotfixes as well, rather than leaving it to the developers to do.)&lt;/p&gt;

&lt;p&gt;One thing to note, despite the name being XM Cloud, Sitecore is positioning this as the evolution of XP, the version of the current product with the analytics mixed in. You won't have to deal with the morass of app services and databases anymore, though, which is a definitely upgrade for your sanity.&lt;/p&gt;

&lt;p&gt;Setting up new sites will be pretty straightforward too, at least their structures, with a wizard setup. If you're familiar with SXA, you'll find things will start falling into that model. This will put more management of the site into the business' hands. There will also be a new Pages interface, the evolution of the experience editor and Horizon editor, but don't worry the content editor is hanging around. Also, Sitecore is encouraging more connectivity with webhooks and APIs, part of the composable idea of linking in whatever service(s) you need with XM Cloud as your central CMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is It Ready?
&lt;/h2&gt;

&lt;p&gt;If you have a basic site, then XM Cloud may very well be ready for you now. But a lot of us that use Sitecore don't get that luxury, and beyond that there are considerations you'll need to take into account.&lt;/p&gt;

&lt;p&gt;First is this, as of this writing, there's no support for item security on the front-end of the site, nor for extranet logins native to Sitecore (IE the core/security database). So if you use a login system with Sitecore roles today to determine what content folks see, it's not there yet. What you need to keep in mind, there's no content delivery server as you know it, but static pages that uses Graph QL for interactivity. There's no web database being looked up or the like.&lt;/p&gt;

&lt;p&gt;There's also no index available by default; the Solr indexes are only available to the CM. This means the old ContentSearch APIs are no longer valid, if you did searches that way or looked up data in buckets. Graph QL has a forward-only query method that you can use in the buckets type case, but if you made your own search page experience with facets, it's obsolete. Sitecore will be offering a Search product soon, which relies on page crawling...think Coveo if you've used that. You can use Sitecore Search, or Coveo, or SearchStax has a crawler/hosted search page, etc...that's the idea of the composable stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are You Ready?
&lt;/h2&gt;

&lt;p&gt;In a similar vein, you have to remember that if you're on the "legacy" Sitecore with MVC today, any move to XM Cloud will never be just an upgrade. XM Cloud only supports the headless technology, so your application will have to be rewritten. While Sitecore says any headless protocol will work, I'll tell you that most folks pushed the JSS/NextJS approach during Symposium. While I know the .NET Core Rendering SDK method is out there, I'd take a hint from what Sitecore folks were mentioning more as far as future-proofing is concerned.&lt;/p&gt;

&lt;p&gt;One good thing is Sitecore did announce the "bridge" from the MVC world to the headless world, in the form of Sitecore 10.3, due out in a few weeks. The big news here was that it will include headless SXA, which is a basis of XM Cloud. If I had any advice, I'd get this and bone up on headless using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Will XM Cloud Be Ready?
&lt;/h2&gt;

&lt;p&gt;That really depends on your needs. Keep an eye out for how the product evolves. But based on the containers approach that was pushed ultra-hard after 10.1 was released, then backed off on, I'd say to give Sitecore a year to really get some feature parity, or workarounds in place, to handle things that have been commonplace to the "legacy" developer. Meantime, get yourself a copy of 10.3 and start learning that headless world. You can always build headless there and push that product out, and sell it to your clients as making the transition to XM Cloud much easier later on if that's how they choose to proceed; if they don't go there, it's at least more up-to-date as a tech stack than .NET Framework 4.8 stuff is.&lt;/p&gt;

&lt;p&gt;I'll try to write some more about some other big idea areas coming out of Symposium. My approach to all of this is pragmatic...I want folks to advance on the tech stacks for sure, but realize that some stuff isn't quite ready for prime time in certain circumstances. Do your research, talk to your Sitecore rep, read what others have to say out there. If you know what the limitations are or the rework needed, you're better informed to know if you should go now or stand pat and wait.&lt;/p&gt;

</description>
      <category>sitecore</category>
      <category>headless</category>
      <category>xmcloud</category>
    </item>
  </channel>
</rss>
