Community blogs

Articles and updates from people in our SUGNL community, and it is open to others as well. We currently show posts from the last 12 months to keep this overview relevant. Want your RSS feed added too?

Please reach out to info@sugnl.net.

Filter by Source

Jan Bluemink · Oct 12, 2026

Sitecore Content and Layout Migration Part 4: Lessons from Practice

Read post

Ronald van der Plas · Oct 8, 2026

Building Agent rendering in Next.js and Sitecore

We've now come to the third and final part of our agent rendering series. In the first blog post, I wrote about the modern DXP frontends and how it's not always easy for AI agents to consume our content. In the second part, we've taken a look at the architecture that we need for this. In this final part, I've built a Proof of Concept to show how it might work. For the Proof of Concept, I used the Sitecore Next.js Skate park starter, this is a starter that is provided by Sitecore to help you get up-and-running for a headless frontend against SitecoreAI. In this starter, I implemented two separate capabilities: a consumer mode that allows components to render a flattened, non-interactive representation for AI agents, and HTTP content negotiation so the page can be requested as Markdown. You can find the implementation in my repo kit-nextjs-skate-park example on GitHub. The implementation deliberately keeps the normal human experience untouched. The last thing you want to do, is ruin your current site and the outstanding ranking that it might have. Therefore, a regular browser will still get the same rendering of the components as before. An AI agent can get a different representation of those same components, with interaction removed and hidden content exposed. When that agent asks for text/markdown, the page will be rendered first through the agent-friendly path before being converted to Markdown. One pipeline, two rendering outputs When first thinking about a proper solution to fix this. I thought about creating a separate pipeline to support the Markdown rendering. That would have created a whole new, separate pipeline to maintain. The first problem is not HTML versus Markdown. The first problem is that some components are designed around human interaction. A navigation menu may require a click, a carousel may only show one slide at a time, another component may even fetch additional information after it has rendered. Simply converting the initial HTML to Markdown does not solve any of that. So I ended up with the following concerns: Consumer: default | agent, Representation: HTML | Markdown The consumer mode decides what the component should render. Representation decides how the result will be delivered. When you look at the code, you will find that the consumer detection lives in consumer-mode.ts, and the AI-specific User-Agents are stored in ai-user-agents.ts. This is how the flow is implemented: The distinction between consumer and representation turns out to be important. The agent version of a component is allowed to have a different DOM structure, but it must remain the content equivalent to the human version. A carousel can become a simple stack of slides, a hamburger navigation will become an expanded list. If a component loads information through client-side calls, its agent representation must retrieve that information server-side instead. Allowing the agent to have all the relevant information it needs to provide good possible context for constructing an answer. Below here, you can see the flow how it goes: By using this flow, we keep the logical in the rendering component for agent or humans, allowing for an easier overhaul to output Markdown rather than HTML. By doing so, we only need to have one function to convert all components into markdown, rather than having each component create it's own markdown representation. Of course you could also do it like that, but I think you might end up in a maintenaince nightmare real soon. Detecting the consumer In order to create an implementation for detecting which type of consumer was accessing our site, I needed to understand how we could identify each one. For this, I've created a list of AI agents types and stored them in ai-user-agents.ts. The list is kept deliberately small and explicit, I don't want to change the flow for none AI user agents, that might cause an unwanted side effect. export const AI_AGENT_USER_AGENTS = [ 'GPTBot', 'ChatGPT-User', 'ClaudeBot', 'Claude-Web', 'anthropic-ai', 'PerplexityBot', 'Google-Extended', 'FacebookBot', 'cohere-ai', ] as const; A deliberate decision I took, was to keep the classic search-engine crawlers out of this list. Agent mode is not meant to become a generic "bot mode" for now. Search crawlers are becoming more able to work with Javascript actions as well, I don't want to risk harming your SEO ranking in the process. Let's move on, on how I implemented the user-agent decetion in consumer-mode.ts. This code turns the User-Agent detection into a typed value: export type ConsumerMode = 'default' | 'agent'; export interface ConsumerContext { mode: ConsumerMode; } export const detectConsumerMode = (headers: Headers): ConsumerContext => { const userAgent = headers.get('user-agent') ?? ''; return isAiAgentUserAgent(userAgent) ? { mode: 'agent' } : { mode: 'default' }; }; From this point on, we can start creating the implementation into the components. Adding consumer mode in Sitecore components To determine the type of consumer requesting the content, I wanted to avoid introducing a separate React Context provider purely for agent rendering. Every Sitecore component already has access to the current page through useSitecore(), so it made more sense to extend the existing page context with the consumer information. I therefore introduced a ConsumerContext type and extended the Sitecore page object with a consumer property. There was, however, a TypeScript issue I encountered with this approach. The SDK's Page is a type alias rather than an interface, so declaration merging was not an option. The solution was to define a local page type that extends the SDK type and becomes the application's source of truth: import { Page as SdkPage } from '@sitecore-content-sdk/nextjs'; import { ConsumerContext } from 'lib/consumer/consumer-mode'; You can see that in src/lib/component-props/index.ts. The actual page route then detects the consumer and adds the result to the page before it is passed into the existing Sitecore providers: const headers = await nextHeaders(); const pageWithConsumer: Page = { ...page, consumer: detectConsumerMode(headers), }; The wiring lives in src/app/[site]/[locale]/[[...path]]/page.tsx. From that point onwards, any component can make one simple decision: if (getConsumerMode(page) === 'agent') { // render the agent representation } Flattening interactive components For the PoC, I implemented the pattern in two components: Navigation and Carousel. For the navigation, the default experience exposes the interactive navigation behaviour, including a hamburger-style interaction for smaller screens. That is useful for a person, but not needed for an agent that doesn't need the UI experience. So when in agent mode, Navigation.tsx renders an expanded recursive list instead. The underlying content does not change; the links are all the same, we only removed the experience around it. In the Carousel.tsx component, we did more or less the same thing. The component uses an interactive carousel with previous and next controls. For a human, that is fine, for an agent, hiding the two other slides behind a next button makes little sense. So when run in the agent mode, the code simply stacks every slide in document order: const CarouselAgent = ({ slides }: { slides: CarouselSlide[] }): JSX.Element => ( {slides.map((slide) => ( ))} ); You might think, but is that really a huge improvement? Perhaps not really, I would have been better to use an example that also uses client-side loading, but you get the idea: don't create an alternate version of every component by default. Most components are probably already fine. Only introduce an agent representation where interaction, client-side loading or presentation hides meaningful information. The most important rule is content parity; ensure that every content remains in both representations. Rendering marking to the consumer Now that we've flattened our components for agent mode, we can continue one step further and render the content out as Markdown. Making our content even better to read for agents. For this to work, I didn't want to expose a new URL structure; you should simply be able to request a page, where, based on the Accept header, the rendering shifts from HTML to Markdown. In order to do this, I've altered the next.config.ts to use a header-based rewrite. { source: '/:path*', destination: '/api/agent-markdown/:path*', locale: false, has: [ { type: 'header', key: 'accept', value: '(.*)text/markdown(.*)', }, ], } The caller still requests:/Carousel, but can ask for another representation by: Accept: text/markdown. That request is internally routed to src/app/api/agent-markdown/[[...path]]/route.ts, therefore ensuring that the public URL never changes, based on the Accept header. Why render HTML first? Like I said before, I wanted to create a simple implementation that would work independently of the components. One might argue that it might be simpler to create a separate rendering that would fetch the raw Sitecore layout data and convert fields directly to Markdown. I deliberately did not want that, because of component behaviour. A component can do more than render the fields Sitecore originally gave it. It can resolve references, call another service, enrich data server-side, or replace client-side fetching with a server-side equivalent in agent mode. If Markdown starts from raw layout data, all of that is bypassed and needs to be rebuilt again. Or become highly intertwined with the default mode, waiting for a nightmare to happen. Instead, the Markdown route asks the application to render the same page again as HTML. The helper for that lives in render-agent-page.ts. It forwards only the request context that matters: copyHeader(request, headers, 'user-agent'); copyHeader(request, headers, 'accept-language'); copyHeader(request, headers, 'cookie'); copyHeader(request, headers, 'authorization'); headers.set('accept', 'text/html,application/xhtml+xml'); headers.set('x-agent-markdown-render', '1'); The original User-Agent is particularly important. If the original request came from GPTBot, the internal HTML render also comes from GPTBot. That means the normal page pipeline detects agent mode again, so the Navigation and Carousel components automatically render their flattened versions, thus making our life simpler with one implementation to maintain here. The Markdown implementation therefore inherits the agent rendering for free. GET /Carousel Accept: text/markdown User-Agent: GPTBot ↓ Markdown rewrite ↓ Internal HTML render User-Agent: GPTBot Accept: text/html ↓ Agent-aware components flatten themselves ↓ Flattened HTML ↓ Markdown conversion The x-agent-markdown-render header is used as a recursion guard. If you've ever written recursive code, you might learned that hard way that it needs a stopping point... The internal request hits the same origin and pathname, so there needs to be a way to distinguish the internal HTML render from the original Markdown request. This approach does have a cost: one Markdown request results in an additional internal HTTP round trip. For a production implementation, that deserves measurement and probably optimisation, for example through caching. For this proof of concept, I preferred cleanliness and simplicity to understand the concept. Turning the rendered page into Markdown Now that we've the flattened HTML from our site, the final step is straightforward. We've built code to do the conversion in html-to-markdown.ts. Here we use Turndown together with the GFM plugin: const turndown = new TurndownService({ headingStyle: 'atx', bulletListMarker: '-', codeBlockStyle: 'fenced', emDelimiter: '*', strongDelimiter: '**', }); turndown.use(gfm); In order to further strip down the HTML, we look for Scripts, styles, templates, SVG markup and other irrelevant elements, which are removed before the HTML is converted to: Content-Type: text/markdown; charset=utf-8 There was only one minor problem when using Turndown. Removing the head section wasn't reliable enough in Node. The Title and metadata content could still end up in the Markdown output. So to guarantee a proper output, we strip the head block before Turndown sees it and keep the Turndown removal rules as a second line of defence. That is not the kind of thing that appears in the architecture diagram, but it is exactly the kind of thing you discover once you actually build the feature. Testing the solution The tests focus less on what the markup looks like and more on whether the contract still holds. consumer-mode.test.ts checks that the known AI User-Agents activate agent mode, while normal browsers and classic search crawlers do not. Next to that, the tests in Navigation.agent-mode.test.tsx checks that agent mode exposes the same links as the default navigation. Ensuring we don't break our existing SEO while outputting a different representation. Same content, different DOM shape. The point is not to create a stripped-down page that happens to be easier for a crawler. The point is to expose the same meaningful content without requiring human interaction to reach it. What to do before going to production? This is a proof of concept, nothing more. You should not put this into production, before making changes to performance and such. Also, ensure that you have at least a baseline of your site. That might include performance SEO, ranking and more. Once yoy have that in order, there are a few things I would want to improve before releasing into production. The first item on the list is caching. The Markdown response currently uses private, no-store. That is the safest starting point when request-time context, personalisation, or enrichment might affect the output, but it is not necessarily where I would leave it. A real implementation needs an explicit caching and revalidation strategy. Another thing to keep an eye out is personalisation, or A/B testing. What version are you going to show to an AI bot? Or perhaps even leave out the whole thing. It's up to you to decide. The next item is the internal HTTP round trip. It is a very convenient way to reuse the complete Next.js and Sitecore rendering pipeline, but it adds latency and work. If this became a heavily used capability, I would investigate extracting the agent representation and server-side enrichment into shared server functions so HTML and Markdown can serialise the same enriched model without requesting the application over HTTP. I would also make the consumer detection more configurable. User-Agent detection is a useful starting point, but the architecture should not depend on it forever. A future version could allow explicit agent-mode negotiation through a request header as well. Also, move the configuration to the configuration, rather than hardcoding it into the solution. Finally, this implementation currently lives only in the skate park starter. I deliberately kept the scope small, so I could prove the pattern before trying to generalise it across other starters. Source code The complete source code is available here: github.com/rvdplas/agent-rendering/tree/main/examples/kit-nextjs-skate-park. I mentioned earlier, this code is Proof of Concept. It is at best a reference implementation of the idea, including the trade-offs and some of the rough edges that came with building it. Where this leaves the idea When I started this series, the question was whether modern DXP frontends should start treating AI agents as another type of consumer. After building the proof of concept, I think the architecture is less exotic than it first sounds. The CMS does not need to change. The structured content does not need to be duplicated. The human experience does not need to be compromised. What changes is the assumption that every consumer needs the same representation. For a human, an interactive carousel makes sense. For an agent, a list of the same slides may be better. For a browser, HTML is the obvious representation. For an agent, Markdown may be easier to consume. The content can remain the same while the rendering layer becomes more aware of who, or what, is asking for it. That is the part of this experiment I find most useful. Agent-ready rendering does not need to become a separate delivery platform. In a modern headless architecture, it can simply become another capability of the frontend. With that said, it's a wrap on agent-ready rendering. Thank you for reading!

Read post

Ronald van der Plas · Oct 1, 2026

SitecoreAI is changing PipelineArgs serialization. What to do?

A small serialisation change with a bigger meaning Sitecore recently announced a change to how the PipelineArgs are serialised and deserialised in SitecoreAI. The Newtonsoft.Json configuration that currently uses TypeNameHandling.All will move to TypeNameHandling.None. For most implementations, this will probably go unnoticed, especially if you haven't deployed custom pipelines. Primitive values such as strings, numbers and booleans will continue to behave as expected. The main difference is that .NET type names are no longer serialised. The interesting part begins when custom code stores complex .NET objects in PipelineArgs.CustomData and later expects those objects to survive a serialisation and deserialisation round-trip. That pattern can depend on type information being written into the JSON itself. Once Sitecore removes that type metadata, the object may no longer come back as the same concrete .NET type. At first glance, the natural reaction is to treat this as a compatibility problem. Find the affected code, adjust the serialisation logic, test the result and move on. Technically, that is a valid response. Architecturally, however, I think it misses the more important point. If this change breaks custom in-process code in a SitecoreAI implementation, the first question should not necessarily be how to make that code work again. The first question should be why that code is there in the first place. That distinction matters because SitecoreAI is not simply Sitecore XP hosted somewhere else. The platform moved to a different hosting model, where custom business logic should increasingly live outside the managed runtime and integrate through supported APIs, webhooks and services. A breaking serialisation change can therefore be more than an upgrade task. It can reveal an architectural dependency that should probably have been removed already. How to test the new behaviour Sitecore provides a feature flag that allows you to enable the new behaviour before it becomes the default. You do this through a normal Sitecore configuration patch: true Deploy that configuration to a non-production environment and exercise the parts of the solution that contain custom pipeline logic, especially anything that stores complex objects in PipelineArgs.CustomData and expects those objects to be reconstructed after serialisation. If nothing breaks, you are probably done. If something does break, Sitecore's guidance is to update the affected customisation so it no longer relies on embedded type metadata, for example by using strongly typed models or explicitly reconstructing the expected object type after deserialisation. That is the short-term compatibility path, and it is useful because it gives customers time to find issues before the new base image becomes the default. For the full rollout guidance and configuration details, refer to Sitecore's original advisory and documentation. My conclusion: stop deploying your own code to SitecoreAI This is the part I think matters more than the serializer change itself. If enabling this flag exposes a custom pipeline dependency, I would not automatically treat that as a signal to modernise the pipeline code. I would treat it as a signal to remove the dependency. SitecoreAI is a managed SaaS platform. The further your implementation moves away from custom code running inside that platform, the better. Custom business logic should live in services you own and control, and SitecoreAI should be accessed through supported contracts such as APIs, webhooks and application extensions. That separation is important for more than just this specific change. Every piece of code deployed into the Sitecore runtime increases your dependency on internal implementation details. Today it is TypeNameHandling. Tomorrow it might be another framework change, another runtime constraint or another security hardening measure. If your customisation reacts to an event, use a webhook. If it needs to read or modify content, use the SitecoreAI APIs. If it performs business logic, move that logic into an external service. If it provides a larger extension, build it as an application instead of embedding it in the platform runtime. Of course, there will be older implementations where that migration cannot happen immediately. In those cases, fixing the serialisation logic may be a perfectly reasonable temporary measure. But it should remain temporary. Please advice the team to log this kind of technical debt, put in on your backlog. Do not invest more than necessary in keeping legacy in-process customisations alive. Use the feature flag to find them. Put them on the board. Fix them if you need time. Then remove them.

Read post

Ronald van der Plas · Sep 30, 2026

Architecting for Agent Rendering

In the previous blog post, I looked at a question that started with Scrunch AXP: if AI agents are becoming another consumer of our digital experiences, should we keep transforming websites afterwards, or should we start designing our frontends with those consumers in mind from the beginning? My conclusion was fairly simple. We should not create a second website for AI agents, and we should not create a second editorial process either. The CMS should remain the single source of truth. The content stays the same, but the representation will change depending on the consumer. For a human visitor, that representation might contain navigation, carousels, tabs, personalisation, and all the other things we have spent years building into modern digital experiences. An AI agent doesn't need many of those interactions. It might benefit more from a stable, semantic and complete representation of the same information. I called the capability agent rendering. The next question is: what architecture do we need to accomplish this? We are not building two websites This is the most important constraint. If every component suddenly gets a completely separate "human version" and "agent version", editors start writing different content for agents, and every new feature needs to be implemented twice. This will surely mean the architecture has already failed. The outcome should be much simpler. The CMS provides structured content, the frontend understands that content, and the rendering layer optimises it for a particular consumer. Conceptually: CMS → component data → rendering layer → representation The representation comes after the content. We are not creating another content model specifically for AI agents. Separate detection from rendering One of the first decisions is where we determine what kind of consumer is making the request. A CDN or edge layer could inspect it, middleware could do it, or the application itself could decide which representation to return. For existing enterprise websites, doing this at the edge can make a lot of sense. That is part of what makes an approach such as Scrunch AXP interesting: you can introduce agent-friendly delivery without rebuilding the systems behind it. For a new headless implementation, I would argue that we might want to take matters into our own hands. But I would not tightly couple the rendering architecture to bot detection. User agents change, new agents appear, and not every agent will identify itself in the same way. One part of the architecture should therefore determine which representation is requested, while another part knows how to render that representation. The rendering logic shouldn't care whether that decision came from a CDN, middleware, a request header or something else. The component data stays the same This is where headless architectures give us an advantage. By the time a frontend receives content from a CMS such as Sitecore or Storyblok, it already knows what that content represents. It knows that something is a hero, a product list, an accordion or a comparison component. That knowledge exists before the frontend turns the data into the visual experience we see in the browser. Take an accordion, for example: the CMS might provide five items, each containing a heading and some content. For a human visitor, we render those items as an accordion because it makes the page easier to scan. But the underlying information isn't really an accordion. It is five sections of related content. An agent renderer could simply expose those five sections as headings followed by their content. Nothing changes in the CMS or editorial process. Only the representation changes. The same applies to tabs, carousels, selectors and other interactive components. Only specialise where it is needed That doesn't mean every component needs a separate agent renderer. A heading is still a heading. A paragraph is still a paragraph. An image with useful alternative text might already be perfectly understandable in its normal HTML representation. Special handling becomes useful when human interaction makes information harder to access. An accordion is an obvious example. A carousel containing several products might be another. A comparison where information only appears after selecting different options is an even clearer case. So instead of asking how we create an AI version of every component, I think the more useful question is: Which components currently hides information behind human interaction? That gives us a much more manageable problem. What should the agent representation look like? I don't think there is a final answer yet. My starting point would still be good semantic HTML. It already works across the web and allows us to preserve hierarchy, links, media references and structured information without inventing a completely new publishing format. But the architecture shouldn't depend on semantic HTML alone. Markdown is interesting because it offers a simpler representation of textual content. Structured data can even provide extra context, and content negotiation could eventually allow a consumer to explicitly request another representation of the same resource. The important part is not whether the first version returns HTML or Markdown. It is having a clear point in the frontend where a representation can be created for a specific consumer. Keep it deterministic For the first version, I would keep the agent representation deliberately boring. No personalisation. No A/B testing. No user-specific state. No generative rewriting during the request. If the published CMS content hasn't changed, the output should ideally stay the same as well. That gives us a canonical representation which is easier to cache, test and monitor, while the approved CMS content remains the source of truth. This also prevents agent rendering from quietly becoming another content channel. The moment we start generating alternative marketing copy specifically for AI agents, we introduce new questions around approval, compliance, translations and ownership. That might become interesting later, but I wouldn't start there. Caching and testing become easier A stable representation also makes caching much more attractive. Human experiences often contain personalisation, experiments and other state that make responses vary between visitors. A public, canonical agent representation can avoid most of that. Instead of letting every agent execute the full frontend and trigger all backend calls used by the human experience, we may be able to serve a much smaller cached response. At the same time, we need to prove that simplifying the representation hasn't removed important information. Because both versions use the same source content, much of that testing can be automated. We can compare important text, links, media references, heading structure and structured data between both representations. The goal isn't to create the smallest possible page. It is to create the simplest representation that still preserves the meaning. The architecture I would propose today When designing a new headless DXP today, I wouldn't make agent rendering a completely separate subsystem. I would make it a capability of the rendering layer. The CMS would continue managing structured content, workflows, translations, permissions and publishing. The frontend would continue receiving component data, but it would no longer assume there is only one way to turn that data into an experience. Most components would have one representation (semantic HTML). Only the components where interaction hides information would need something different. Consumer detection could happen earlier at the CDN, edge or application level, while the rendering layer remains independent of how that decision was made. I am not suggesting this because we already know exactly what AI agents will need in the future. We don't. The value is that the architecture gives us a place to evolve without redesigning the frontend later. From architecture to implementation All of this still looks relatively clean on paper. The real test is what happens when we actually build it. How much additional code does an agent renderer require? Can we avoid maintaining two separate component trees? What happens to components that don't have a special agent representation? And how do we test that both versions still contain the same meaningful information? That is what I want to explore in part 3. Instead of another architecture diagram, I want to build a small agent-rendering implementation using a modern headless frontend. We can take a few typical interactive components, render them normally for a human visitor and then create a deterministic representation for an AI agent. That should show whether agent rendering remains a useful architectural idea once we actually have to maintain the code.

Read post

Rob Habraken · Sep 23, 2026

Tracking Asset Usage: Content Hub UI Customizations

In my previous posts, we built a serverless pipeline that captures Sitecore publishing lifecycle telemetry and handles asset state changes via an Azure Function middleware layer. The final step of our infrastructure calculates deltas on the fly and saves the active tracking footprint directly into a secured, custom JSON property (UsageTracking) on the M.Asset schema […]

Read post

Ronald van der Plas · Sep 21, 2026

Your DXP was built for humans. What about AI agents?

Recently, I watched a Sitecore MVP webinar about "Meet Scrunch: The Future of AI Discovery". The session had a lot to think about, but one part in particular stuck with me: Scrunch's Agent Experience Platform, or AXP. One thing AXP can do is give AI agents a different representation of a webpage. Instead of sending all the JavaScript, tracking, interactive components and other browser-oriented complexity that we normally deliver to a human visitor, AXP can serve clean, server-rendered HTML without a JavaScript dependency. Dynamic components can also be simplified or restructured so their information is easier for an AI agent to consume. I really like that idea. Especially for large enterprise websites, where you might have years of technology decisions behind you, multiple platforms, JavaScript-heavy applications and perhaps no headless architecture yet. Then putting an intelligent delivery layer in front of what already exists would be a very pragmatic solution. You can improve what agents receive without rebuilding your entire website. But while listening to the webinar, it also made me think about the other side of this problem. If I were designing a headless DXP from scratch today, would I still want to add another platform dependency to transform my pages? Or should the frontend be able to serve AI agents properly from the start? We designed our digital experiences for humans For years, most of our assumptions have been relatively simple. Someone opens a browser, requests a page and then interacts with the experience we have carefully designed for them. You can see that assumption almost everywhere in modern websites. We use JavaScript to provide interaction. We hide information behind tabs and accordions, just to create a seamless experience. We use carousels to fit multiple products into a limited amount of space. We personalise components, run experiments, add tracking and keep making the frontend experience more sophisticated. There is nothing wrong with that. All of those things can provide real value to a human visitor. But AI agents have very different needs. It doesn't need an accordion to save screen space. It doesn't care about the animation between two slides in a carousel. It doesn't need a fancy menu to figure out where to click next in the same way a person does. It can do without all those fancy experiences that we keep adding to make our customers happy. What it does need is access to the information, plus enough structure and context to understand what that information actually means. And that is becoming more relevant. In July 2026, Cloudflare reported that automated bot traffic had surpassed human activity and was generating roughly 57% of all web requests. That includes far more than AI agents, so we shouldn't translate that number into "57% AI traffic". However, it does show that websites increasingly have machine consumers next to human ones. If that continues, our DXP architectures probably need to start acknowledging it as well. Smaller HTML is not the actual goal It is tempting to reduce this whole problem to something like "remove JavaScript for AI". I don't think that goes far enough. A small HTML document isn't automatically a useful document. Imagine an accordion containing important information, where only the first panel is available unless someone interacts with it. Removing the JavaScript does not suddenly make that page agent-friendly. It might actually make part of the content impossible to access. So the goal shouldn't simply be to create less HTML or remove more JavaScript. The goal should be to give an AI bot access to the better meaning of the content on the page. While removing all the fancy experience that the bot is not interested in. Take a product carousel with five products. A human visitor might see one product at a time and click or swipe through them. An agent doesn't need that interaction. Its representation could simply expose all five products as a semantic list. An accordion could become a series of headings and sections. Tabs could become sequential content. A complex comparison experience could expose the comparison data without requiring the interaction around it. At the same time, a normal content block with a heading and a few paragraphs probably doesn't need any special treatment at all. That is important, because I don't think the solution is to build an alternative version of every component. We only need to look at the places where the human experience gets in the way of accessing the information. Headless gives us an interesting opportunity This is where I think headless architectures become interesting. A typical implementation might have a CMS such as Sitecore or Storyblok providing structured content to a Next.js frontend. That frontend already knows what the content is, which components need to be rendered and what those components are supposed to represent. In other words, we have a lot of knowledge available before everything is turned into the human experience. An external agent-experience layer works from the other direction. It receives the finished website and transforms it back into something that is more suitable for machines. Scrunch describes AXP as sitting at the CDN layer, detecting AI traffic and serving an optimised version while leaving the normal human website unchanged. For existing websites, I think that makes a lot of sense. It allows you to retrofit agent support onto an architecture that wasn't designed for it. For a new headless architecture, however, I think we should at least ask whether some of that responsibility could live closer to the frontend. Instead of thinking only in this direction: structured content → human experience → machine-friendly transformation we could start thinking about: structured content → representation appropriate for the consumer That might sound like I am proposing two websites, but that is exactly what I would try to avoid. Same content, different representation For me, this would be one of the most important architectural constraints. The CMS remains the single source of truth. I would not start by letting an AI representation invent additional content, generate new claims or maintain its own AI-specific editorial version of a page. The moment we do that, we are introducing another publishing channel, with a whole lot of maintenance nightmares. And for a large organisation, that will create a lot of questions very quickly. Which version is correct? Who approves the AI-specific text? Does it go through the same compliance process? What happens with translations? And how do we make sure the human and agent versions don't slowly start drifting apart? I would start much simpler. Same content. Same permissions. Different representation. The agent representation can reorganise existing content, expose information that was hidden behind interaction, improve its semantic structure and remove unnecessary browser machinery. But the underlying facts and access boundaries remain the same. This also makes the architecture much easier to test. We can automatically compare the human and agent representations and check whether meaningful content, links, media references or other context has disappeared. Remove the chrome, not the context Once you start looking at a page this way, there is quite a lot that an agent probably doesn't need. It is unlikely to benefit much from our account menu, cookie interface, animated navigation, tracking scripts or a footer with dozens of repetitive navigation links. Removing those things isn't only about creating cleaner HTML. It could also reduce the work performed by backend services and lower the amount of data transferred for machine traffic. But we have to be careful not to remove useful context at the same time. Breadcrumbs, for example, can tell an agent where information sits inside a site hierarchy. Brand and site identity still matter. Relevant related links can describe relationships between information. Canonical information, language information and structured data should remain available as well. I would also keep references to meaningful images. The consumer can decide whether it wants to retrieve and interpret the actual image. The same applies to video. We don't have to send an entire interactive video player just to communicate that a relevant video exists. So for me the principle is quite simple: Remove interaction and presentation noise, but preserve meaning and context. Keep the agent experience stable There is another difference between human and agent experiences that I think can work in our favour. Our human websites are increasingly dynamic. We personalise content, run A/B tests and adapt experiences based on previous behaviour or other information we have about the visitor. For the first generation of an agent representation, I would probably do almost the opposite. I would keep it boring. Make it canonical, public, deterministic and highly cacheable. No personalisation, no experimentation, no user-specific state. If the underlying content in the CMS doesn't change, the agent should ideally receive the same representation every time. That gives us something we can properly test, cache and reason about. I would also initially keep traditional search crawlers on the rendering path that we already know. Scrunch takes a similar approach with AXP and states that Googlebot and Bingbot are not routed through its agent experience, while real-time AI retrieval agents are. In the future, there might be reasons to give search engines a similar optimised representation. For existing organic search performance is simply too important to include casually in the first experiment. Don't try to fix something that isn't broken. Make it measurable This is also where I think we have to be careful with the claims we make. Removing JavaScript doesn't prove that ChatGPT will suddenly cite your website more often. Better semantic HTML doesn't automatically mean better visibility in Gemini either. Generative systems are probabilistic, and there are many factors outside our own website that influence the answers they produce. But there are things we can measure. Can we remove the client-side runtime without losing meaningful content? Can we expose information that was previously hidden behind interaction? Is the resulting structure easier for a machine to understand? Is the output stable? Is the payload smaller than the normal human version? Those are concrete engineering outcomes. From there, we can start observing what actually happens and keep improving it. Baseline, optimise, observe and repeat. For me, that is a much more useful approach than pretending we already know exactly how every AI agent wants to consume the web. Start putting this in your proposals I don't think we should wait until all standards and best practices around this have settled. If I were proposing a new headless DXP implementation today, I would already make agent readiness part of the architecture discussion. That doesn't mean promising a second rendering implementation for every component. It doesn't mean putting generative AI directly into the live request path. And it certainly doesn't mean building a second website specifically for bots. It simply means acknowledging that the browser is no longer the only consumer we have to think about. We should identify experiences where human interaction hides information. We should think about what a canonical machine representation could look like. We should keep the CMS in control of the content. And we should design the frontend in such a way that this capability can evolve when agent behaviour and web standards change. AI-assisted development is here to stay; using it makes this more interesting. Writing and maintaining an alternative representation for the small number of components that actually need one is becoming cheaper. Personally, I would much rather use AI to help developers create and test deterministic rendering code than use generative AI to rewrite approved brand content on every request. Scrunch AXP was what triggered this thought process for me, and I still think its approach makes a lot of sense, especially when you have to make an existing enterprise landscape more agent-ready without rebuilding it. But when we are designing a new headless architecture, we have the opportunity to think about this earlier. Maybe supporting both humans and machines should simply become another responsibility we expect from the frontend. I have started calling that capability agent rendering. In the next article, I will go deeper into what an agent-rendering architecture could actually look like. Where should consumer detection happen? How can components provide another representation without doubling the maintenance? How should we handle caching and testing? And where could emerging capabilities such as Markdown content negotiation fit into the picture? Because if AI agents are becoming another consumer of our digital experiences, the question is no longer only whether they can access our websites. The more interesting question is what experience we choose to give them.

Read post

Jan Bluemink · Sep 5, 2026

Using the Sitecore Content Transfer API Locally with Docker

Read post

Ronald van der Plas · Aug 18, 2026

Content Insights Tip #84 | Sitecore moves Explorer retirement date forward to September 22

Sitecore has updated the retirement date for the Explorer application in SitecoreAI. Explorer will now be retired on September 22, 2026, earlier than previously communicated. The current Sitecore documentation still lists October 1, 2026 as the date on which Explorer will be deprecated, so this change moves the deadline forward by just over a week. Sitecore has indicated that after September 22, Explorer will no longer be available for access or use. If you are still using Explorer as part of your content management workflows, it is worth reviewing those workflows now and making sure they have been migrated before the new deadline. Page builder Content mode is the supported experience moving forward in SitecoreAI, so teams still relying on Explorer should make sure they are comfortable with the replacement before September 22.

Read post

Jan Bluemink · Aug 15, 2026

Sitecore Content and Layout Migration Part 3: Sitecore PowerShell Extensions

Read post

Ronald van der Plas · Aug 10, 2026

New in Content Hub Power Extension: Cheat Sheets

I’ve released a new version of the Content Hub Power Extension, now including a new Cheat Sheets section. The first cheat sheets focus on Scripting, Triggers and Actions, giving you a quick reference while working with Sitecore Content Hub. Instead of searching through documentation or old examples, you can keep the most useful information close at hand while building and configuring your solution. This is only the start. I plan to expand the cheat sheets with more useful Content Hub topics over time. If you’re already using the Content Hub Power Extension, make sure to update to the latest version and give it a try!

Read post

Ronald van der Plas · Jul 30, 2026

Content Insights Tip #83 | Bulk vs Non-Bulk in Sitecore Content Hub: Choosing the right execution strategy

When creating an External Mass Action in Sitecore Content Hub, one of the configuration options you'll encounter is the Bulk toggle. It's easy to overlook. After all, it is just a single checkbox in the Selection component, surrounded by several other settings that appear equally important. Yet this checkbox has an impact that most people might not realise. It doesn't simply determine whether multiple assets can be processed together. Instead, it changes how Content Hub communicates with your external service and, more importantly, where the responsibility for processing those assets lives. At first glance, the difference seems straightforward. With Bulk disabled, Content Hub sends a single request for every selected asset. Enable Bulk, and those assets are grouped into a single request. While that's technically correct, it doesn't explain why Sitecore offers both options or when one approach is more suitable than the other. To answer that question, let's first look at what actually changes. Non-Bulk processing When Bulk is disabled, every selected asset results in its own HTTP request. If a user selects three assets, your endpoint is invoked three times. Each request contains information about a single asset, making every execution completely independent from the others. Request 1 { "TargetId": 34181, "TargetIdentifier": "xEYMBs3UTo6ZDN_Xp8ykgg", "context": {} } Request 2 { "TargetId": 34253, "TargetIdentifier": "o-SNrVDwTRWIzcHG0cu8VA", "context": {} } Request 3 { "TargetId": 34127, "TargetIdentifier": "hqFQTkdmQGeHDIJDzBnHRw", "context": {} } This execution model has an important consequence. Content Hub is invoking your service once for each selected asset. Your service doesn't know how many assets the user selected, because each request always contains a single asset. Bulk processing With Bulk enabled, the interaction changes. Rather than invoking your endpoint once per asset, Content Hub does a single request that contains all selected assets. { "targets": [ { "TargetId": 34181, "TargetIdentifier": "xEYMBs3UTo6ZDN_Xp8ykgg", "context": {} }, { "TargetId": 34253, "TargetIdentifier": "o-SNrVDwTRWIzcHG0cu8VA", "context": {} }, { "TargetId": 34127, "TargetIdentifier": "hqFQTkdmQGeHDIJDzBnHRw", "context": {} } ] } Notice that not only the number of HTTP requests changes, also the JSON contract itself changes. Instead of receiving a single asset, your service receives a collection of assets and becomes responsible for processing them. That distinction may appear subtle, but architecturally it is significant. An execution strategy, not a performance setting One misconception I regularly encounter is that Bulk should simply be enabled for better performance. While it often reduces the number of HTTP requests, that's not the reason why the option exists. The real question is much simpler: Where should the iteration happen? Should Content Hub invoke your service once for every asset, or should your service receive a collection of assets and decide how to process them? That decision has consequences far beyond the transport layer. It affects how you design your queues, how you prioritise work, how you handle failures, and ultimately how your application scales. Consider an AI enrichment service that supports both scheduled processing and urgent requests. Every night, editors submit hundreds of assets for transcription or metadata generation. Those requests are ideal candidates for Bulk processing because the service can optimise throughput and process the work whenever capacity becomes available. Now imagine an editor who needs a single asset enriched immediately before publishing. That request shouldn't wait behind hundreds of scheduled jobs. Instead, it can use a separate Non-Bulk action that places the work directly into a priority lane. Although both actions call the same service, they represent two completely different execution strategies. This is why the choice between Bulk and Non-Bulk should always be driven by functional requirements rather than performance alone. When Content Hub is the caller There's another architectural principle worth mentioning. It can be tempting to assume that because Content Hub initiates the request, the data it sends is always ready to process. In practice, your service should never rely on that assumption. Your endpoint should validate whether an asset is still available, whether it is in the correct lifecycle state, and whether processing it actually makes sense. Likewise, if processing the same asset twice would cause problems, your implementation should be resilient enough to handle duplicate requests gracefully. These responsibilities belong to the service, not to Content Hub. The platform is responsible for invoking your endpoint. Your responsibility is to build an endpoint that behaves correctly under all circumstances. Conclusion The Bulk toggle may be one of the smallest configuration options in Sitecore Content Hub, but it represents an important architectural decision. It determines who is responsible for iterating over assets, influences how your application handles prioritisation and scalability, and shapes the overall execution model of your integration. The next time you create an External Mass Action, don't ask yourself whether Bulk is faster. Instead, ask a different question: Where should the iteration happen? More often than not, the answer to that question will also tell you which option to choose.

Read post

Ronald van der Plas · Jul 30, 2026

Content Insights Tip #82 | Monitor your GitHub Copilot AI credit usage

GitHub Copilot has become an important part of our development workflow in Visual Studio Code. What started as inline code completion has developed into a capable coding agent that can analyse repositories, edit multiple files and execute increasingly complex development tasks. Like many digital services, AI tools were initially priced aggressively to encourage rapid adoption. Now that developers and organisations are becoming increasingly dependent on them, prices and usage limits are gradually increasing. Running these models remains expensive, so it is unlikely that AI usage will become cheaper or less restricted in the near future. That makes it more important than ever to create awareness around token consumption and reduce unnecessary usage. Before we can prevent tokens from being wasted, we first need insight into how many credits we are consuming and which activities are responsible. For developers using GitHub Copilot Business or Enterprise through their employer, reaching a limit will not usually result in a personal bill. It can, however, prevent you from continuing to use Copilot Chat, Agent Mode and other AI-powered features until additional credits become available or the allowance resets. Fortunately, GitHub provides several ways to monitor your usage. Check your Copilot usage in GitHub Open your personal GitHub settings and navigate to: Profile picture → Settings → Copilot → Features. You can also open the page directly: github.com/settings/copilot/features. The Usage section shows how much of your included AI Credit allowance has already been consumed. This is the official usage information connected to your account, so it should be your main reference when checking how close you are to your limit. For professional Copilot plans, inline suggestions and Next Edit Suggestions do not consume AI Credits. The relevant usage comes primarily from features such as Copilot Chat, Agent Mode, Copilot CLI and other model-driven interactions. Check your usage from Visual Studio Code You can also view your current usage without leaving Visual Studio Code. Click the Copilot icon in the status bar to see the limits applying to your account and when they reset. This is useful after a long Agent Mode session or when using more capable reasoning models. A single task may involve several model calls, large amounts of repository context and multiple sub-agents behind the scenes. The built-in indicator tells you how much usage remains, but it does not necessarily explain which sessions consumed those credits. Understand where your credits are going For more details, you can install the third-party Copilot Cost & Token Tracker extension for Visual Studio Code. The extension reads the Copilot agent logs stored locally by Visual Studio Code and creates a dashboard showing: Estimated AI Credits per session Usage broken down by model Input, output and cached tokens Main-agent and sub-agent calls Aggregated usage over time This can help identify why one session consumed considerably more than another. Perhaps Copilot repeatedly loaded a large amount of context. Maybe it used a powerful model for a relatively simple task, created several sub-agents or kept retrying an unsuccessful approach. The extension should be treated as a diagnostic tool rather than an official billing overview. It only sees the sessions available in your local Visual Studio Code logs and calculates its results using its own pricing information. A useful distinction is: GitHub shows how close you are to your actual limit. The extension helps explain which local sessions consumed your credits. As with any third-party extension used in a professional environment, check whether your organisation permits its installation. My thoughts Usage-based pricing is understandable. A short coding question and an autonomous agent working across an entire repository clearly do not require the same amount of computing power. However, we are paying for the tokens consumed, not for the value of the result. An agent can use a large amount of context, call several tools and generate thousands of tokens, only to produce a solution that does not compile or completely misunderstands the problem. Those tokens cost exactly the same as tokens that resulted in a perfect solution. I would like to have the option to rate the value of an AI response. Was it useful, partly useful or completely useless? When a solution is terrible, should we really pay the full price for the tokens used to create it? In practice, this would be difficult to implement fairly. The model provider has already incurred the infrastructure costs, the quality of an answer is subjective, and a refund system would be easy to abuse. LLM providers also cannot guarantee that every answer will be correct. I therefore do not expect a thumbs-down button to return our AI Credits anytime soon. Still, it is an interesting thought. AI providers already ask us to rate their answers because that feedback helps improve their models. Perhaps future usage dashboards should not only show how many tokens we consumed, but also help us understand whether those tokens produced something valuable.

Read post

Showing 1-12 of 54 posts

Page 1 of 5