# Roboto Studio - Complete Content > This file contains all content from robotostudio.com in markdown format. > Optimized for LLM consumption and RAG systems. --- # Services ## Agentic websites > We build fast, type-safe websites engineered for SEO, AEO and conversion. Your AI agent ships landing pages on the rails. Bring v0, Lovable, Cursor, Replit or Codex. **Updated:** 2026-08-25 --- Companies of all sizes trust Roboto Studio An agentic website is one your marketing team builds on with its own AI coding agent. We hand over a fast, type-safe foundation, and your content lives as structured files in your own repository. You point the agent your team already builds with at those files (v0, Lovable, Replit, Cursor, Codex, Claude Code), describe the landing page or campaign you need, and it assembles the page from components we have already tuned for speed and conversion. Today a campaign page often means a brief, a developer queue and a sprint of waiting, so it can go live after the moment that prompted it has passed. With an agentic website you brief your agent and preview the page the same day. Coding agents only recently got good enough to build whole pages from a prompt. Raw agent output tends to drift off-brand, load slowly and ship thin, half-crawlable HTML. The foundation prevents that. It is the rails: typed components, your design system and validation that keep whatever the agent produces fast, crawlable and on-brand before it reaches a visitor. We don't sell you our agent. We build the website yours can safely work in, and that website speaks the plain, structured language every current and future coding agent already reads, with no onboarding call and no proprietary format to learn. When a faster model or a better tool arrives next quarter, you switch to it and nothing about your site has to change, because the rails live in the foundation rather than the agent itself. Hand an unguarded AI tool your homepage and it will happily produce an off-brand, slow, half-crawlable page. We remove that risk at the source. Every page your agent builds is assembled from typed, conversion-tested components and validated against a Zod schema before it can ship. A missing meta description, a broken image path, an invalid layout: each one fails the build instead of reaching your visitors. So your agent moves fast and the guardrails hold. Marketers describe what they want, the agent assembles approved blocks, and the rails make sure the output stays on-brand, fast and ready to rank. Your pages are plain files in your own repository, so you own every word outright. A headless CMS bills you per editor seat and meters your API, so the cost of shipping climbs with every marketer you hire and every page you serve. Here it stays flat: add ten marketers or a hundred, and there is no rate limit on launch day and no vendor sitting between you and your own back catalogue. Every change is versioned, so when a headline edit moves the numbers the wrong way, you can see exactly what changed, who changed it and when, then restore the winning page in seconds. Your campaign copy gets the audit trail your spend reports already have, and nothing you publish is locked inside a tool you would have to buy your way out of. A prompt can produce a page in minutes, which is why an agentic website can look, at a glance, like one more AI page builder. The difference is what survives once you are shipping dozens of pages a month: whether each one stays fast, findable and on-brand, or whether the quality quietly slides as the count climbs. Holding that line is the work the foundation does. Components are checked before a page can preview, so what reaches your visitors looks reviewed even when an agent wrote it in a single pass. That is the difference between a quick draft and a page you can put ad budget behind. | Approach | What ships | | --- | --- | | An AI builder with no guardrails | A quick first draft that drifts off-brand, with thin HTML and performance that slips as pages pile up | | A traditional CMS | A polished editor, plus a dev ticket and a sprint wait for anything the template does not already do | | An agentic website | Your agent on our rails: every page crawlable, fast, on-brand and conversion-ready the moment it ships | My best experience with a consulting company. The results were delivered faster than expected and with top quality. Jono ensured I understood the process and suggested a great approach. Both execution and communication were flawless. Agentic builds we've shipped ## Vercel agency partner A Vercel partner agency, building fast, type-safe websites on Next.js and the Vercel stack that your AI agent can build on from day one. ## Thinking about an agentic website? Here are the questions we hear most often ### Which AI agents can I use? Whichever your team already works in. We have handed sites to teams using v0, Lovable, Cursor, Replit, Codex and Claude Code. The website is plain structured files, so any current or future coding agent can read and edit it. You are never tied to one vendor's tool. ### What stops the agent from making a mess? The rails. Every page is built from typed components and checked against a Zod schema before it ships, so broken metadata, missing images or invalid layouts fail the build rather than reaching your visitors. Each change also lands as a preview you approve before it goes live. The agent moves fast inside boundaries you control. ### Is this what people mean by vibe marketing? It is the part that makes vibe marketing safe to ship. Prompting a page into existence is easy. Making sure that page is fast, on-brand and built to convert is the hard part, and that is what the foundation handles. You get the speed of describing a page in plain English with the quality bar of an agency build. ### Is this the same as your agentic workflows service? No, they pair well but solve different problems. An agentic website gives your team's coding agent a safe foundation to build pages on. Our [agentic workflows service](/services/agentic-workflows) is where we build the agents: content pipelines, lead enrichment, background agents that monitor and act. Plenty of clients start with one and add the other once the first system proves itself. ### What is an agentic CMS? It is a content setup where an AI agent is a first-class editor, not an afterthought. Instead of a proprietary dashboard, your content lives as structured files an agent can read, write and validate. You still get previews, version history and review, with an agent doing the heavy lifting and your team approving the result. ### How does this help SEO and AI search? Two ways. Pages render as fast, fully crawlable HTML with structured data, which is what both Google and answer engines like ChatGPT and Perplexity need to read and cite you. And because new pages are cheap to produce, you can cover the long-tail and programmatic queries that a hand-built site never reaches. ### Do my marketers need to write code? No. They describe the page they want and the agent builds it, the same way they already brief a designer. The rails handle the technical correctness underneath. For teams that prefer a visual flow, we set up preview links so every change can be seen before it goes live. ### Can we move our existing site onto this? Yes. We migrate your current pages into the new foundation, map redirects so your rankings carry over, and hand you a site your marketing team and its agent can build on. Moving off an aging WordPress or legacy CMS does not mean a publishing freeze. ## Bring your agent. We will build the rails. Tell us the tool your team builds with and the pages you need to ship. We will scope the foundation that keeps everything it makes fast, found and on-brand. From the blog --- ## AI automation services > Custom AI automation: production AI agents and agentic workflows on Vercel, built and maintained by the engineers who ship them. **Updated:** 2026-08-25 --- ## The AI automation you prototyped, rebuilt to run in production You've already prototyped the AI feature. We rebuild it to run in production: durable workflows on Vercel that survive deployments, retry failed steps, and keep running under real traffic. The same pattern that briefs our sales team within a minute of a form submit, fills a content calendar overnight, and keeps a product catalogue current without anyone opening a spreadsheet. Companies of all sizes trust Roboto Studio My best experience with a consulting company. The results were delivered faster than expected and with top quality. Jono ensured I understood the process and suggested a great approach. Both execution and communication were flawless. Production builds we've shipped ## How these work in practice Every workflow below runs in production. They survive server restarts, retry failed steps automatically, and pause for external events without consuming compute. Here's what that looks like for real problems. Your marketing team knows they should publish more. They don't have the hours. Here's what we build: a workflow that connects to the Ahrefs API, pulls your keyword gaps and ranking opportunities, then generates research briefs for each topic. A second workflow takes those briefs, researches the subject using AI, writes a first draft, and pushes it to your CMS as a draft post. Set the whole thing on a CRON schedule. Monday morning, your editor opens Sanity and finds five draft posts waiting for review, each targeted at a keyword your competitors rank for and you don't. The AI did the research and the first draft. Your writer does the thinking and the polish. Each step in the pipeline retries independently. If the Ahrefs API rate-limits you, that step waits and retries. If the LLM call fails, it tries again without re-fetching the keyword data. Deploy a code update while a draft is mid-generation? The workflow finishes on the old version. If you operate in multiple markets, the same pipeline can generate localised versions of each post. The workflow takes your approved English draft, translates it, adjusts examples and references for the target region, and pushes each version to the correct locale in your CMS. One editorial review produces content for every market you sell into. When someone submits a contact form on our site, a workflow kicks off within seconds. It extracts the domain from their email, scrapes their company's website for context, then sends everything to Claude. The AI researches the company, looks at what they do, checks for recent news or funding rounds, and generates a structured brief. That brief lands in our Slack within a minute of the form submission. The entire thing is about 40 lines of TypeScript. Each step uses a `"use step"` directive, so if Claude's API is slow or Slack returns a 500, that individual step retries without re-scraping the website. We use this ourselves, every day. For clients, we extend this pattern to push enrichment data into their CRM, score leads based on company fit, and trigger different follow-up sequences depending on what the AI finds. A SaaS company can automatically route enterprise leads to their sales team and self-serve leads to a product tour. Workflows are great for request-response pipelines. Some problems need an agent that lives in the background, watches for changes, decides what to do, and acts. Stale comparison pages that need fact-checking against a competitor's docs. Brand citation alerts in ChatGPT and Perplexity. A llms.txt file that should regenerate every time the product changes. We build these on eve, Vercel's open-source agent framework. eve treats an agent as ordinary files in a TypeScript repo: a markdown system prompt, typed tool definitions, and `defineSchedule` for the cron that wakes it up. Sessions are durable by default, built on the same workflow engine as our pipelines, so an agent halfway through a task survives a crash or a deploy. When it needs to run code, it does so inside an isolated Vercel Sandbox, never against your production environment. The same agent runs locally under `eve dev` and on Vercel in production. Schedules trigger them. Remote webhooks trigger them. Other agents trigger them. Credentials stay out of the codebase. When an agent posts to Slack, opens a pull request, or writes to your CRM, it asks Vercel Connect for a short-lived, scoped token at runtime instead of reading a long-lived secret from an environment variable. The keys stay centralised and auditable, and you revoke access at the provider rather than by redeploying. Roboto's own CMS migration pages are kept current by a weekly background agent that scrapes the source CMS's docs, diffs against our YAML, and opens a pull request when something is out of date. The agent we built for ourselves is the same agent we ship to clients. If you want one watching your competitor's pricing, your product changelog, your brand citations across the AI search surfaces, we build it on the same foundation. An agent without evals is a demo. The moment you connect it to real data, a prompt change can break it silently. We build eval pipelines alongside every production agent: golden datasets of inputs your agent should handle, deterministic checks for the parts you can grade with code, and LLM-as-judge grading for the parts you can't. Every prompt change runs through the eval suite before it ships. Every model upgrade gets scored before you switch over. Regressions get caught before your editor reviews a bad draft or your sales team gets a wrong brief. The eval suite is the closest thing agentic systems have to a test suite. We treat evals as a productised add-on. They can be the starting point of an engagement if you already have an agent in production that isn't trustworthy, or they can be built alongside a new agent from day one. An agent that runs once a week costs nothing worth discussing. An agent that runs on every form submit, every catalogue update and every support ticket has a bill attached, and that bill gets decided early in the build or inherited later at the worst possible moment. So we design for it from day one rather than selling it back to you later as an add-on. Every step picks the model that suits the job: a fast, cheap model handles classification, extraction and routing, and the expensive one is held back for the step that needs judgement. Because the AI SDK is provider-agnostic, moving a step onto a different model is a config change instead of a rewrite, so the routing keeps improving as the model landscape shifts. Prompts get cached where the same context repeats. Each step gets only the context it needs rather than the whole conversation. And every run reports its own cost into PostHog LLM analytics from the first deploy, so the expensive step is visible on day one instead of showing up in a quarterly invoice. If you already have an agent live and the bill is the thing worrying you, that is a perfectly good place to start an engagement. We instrument what you have, find where the tokens actually go, and bring them down, with the eval suite above as the backstop so nothing quietly gets worse on the way to getting cheaper. The stack behind these systems Vercel's open-source agent framework, and what our background agents actually run on. An agent is a directory of files in your repo, so it ships through the same branches, pull requests, and preview deploys as the rest of your code. Durable sessions, sandboxing, human approvals, and evals all come built in. The Workflow Development Kit gives every step automatic retries, durable state, and replay-on-deploy. Open source, runs anywhere, but pairs cleanly with Vercel Fluid Compute. Provider-agnostic model calls, structured outputs via Zod, tool use, streaming, and observability. Switch models without rewriting the agent. Isolated microVMs for code execution inside an agent. Lets agents run shell commands, clone repos, and execute generated code without giving them production access. Short-lived, scoped credentials for agents. Rather than long-lived secrets sitting in environment variables, an agent requests a token at runtime to act in Slack, GitHub, Salesforce, or any OAuth or API-key service. Access stays scoped per project and revocable at the provider. Every model call, every tool invocation, every conversation captured for review. Quality regressions and cost blowouts get caught the day they happen, not the week after. ## Forward deployed engineers, embedded in your team Agentic systems live inside your codebase, your content models, and your observability stack. They need daily iteration on prompts, tools, and editorial review. So we ship them the way Palantir, OpenAI, and Anthropic ship AI: by embedding senior engineers directly into your team for the life of the engagement. A Roboto FDE engagement puts one or two senior engineers into your codebase with the same access as your own team. Shared Slack channel, repo write access, on-call posture for the agents we ship. Weekly demos, daily-or-better async updates, and a documented playbook your team owns when the engagement winds down. We transfer knowledge as we go, so nothing depends on a rushed handover at the end. The cadence matters because agentic systems aren't ship-and-walk-away projects. Prompt iteration runs daily once the agent hits real traffic. Tool integrations break in ways nobody predicts at scoping. Editorial review needs context only your team can give. An external vendor on a weekly call is too slow for that loop. Engagements run a minimum of eight to twelve weeks so the build, observe, and iterate cycle has room to play out. Most settle into a rolling monthly retainer once the first systems are live and the next ones are queued. CMS migrations, headless Shopify builds, and Contentful implementations all ship cleanly as projects with handoff boundaries. Agentic work doesn't. The output is your voice, your data, and your customer-facing automation, and it changes weekly based on what real usage reveals. That's why FDE applies to agentic workflows and AEO engagements, not to every service Roboto offers. If you're after a fixed-scope build with a clean handoff, our project teams handle that. If you're after an agent or workflow that needs to keep getting better in production, you want the embedded model. ## Vercel agency partner A Vercel partner agency, shipping production agents and workflows on Vercel's AI infrastructure since the first release of the Workflow Development Kit. One of the background agents we set up most often is a page updater: it researches new information and updates a page to match. Our own CMS freshness bot, the one that keeps the migration comparison pages current, is a tailored version of exactly this. ## Thinking about agentic workflows? Here are the questions we hear most often ### What does a Forward deployed engineer engagement actually look like? One or two senior Roboto engineers join your Slack, your repo, and your standups for the life of the engagement. We treat your codebase like ours: branches, pull requests, code review, deploys. Weekly demos cover what shipped, what's queued, and what decisions need your input. Daily async updates keep you ahead of the work. Minimum engagement is eight to twelve weeks; most settle into a rolling monthly retainer once the first agents are live. ### How is this different from a typical AI automation agency? Most AI automation agencies sell no-code workflows on Zapier, Make, or n8n. Useful for prototypes, fragile in production. We ship typed TypeScript on Vercel's durable workflow runtime, with eval pipelines, observability, and version control. The systems we build live inside your codebase, get reviewed in your pull request flow, and survive your deploys. Different toolchain, different reliability bar. ### What is eve? eve is Vercel's open-source agent framework, and it's what our production agents run on. It treats an agent as a directory of files in a TypeScript repo: a markdown system prompt, typed tools, schedules, and skills. The framework handles the parts every serious agent needs anyway, like durable sessions that survive a deploy, sandboxed code execution, human-in-the-loop approvals, OpenTelemetry tracing, and an eval harness. Because an agent is just code in your repo, it gets versioned, reviewed, and deployed like everything else you ship. We run our own background agents on eve, so the patterns we bring to your build are ones we've already debugged on ourselves. ### What is a durable workflow? A program that saves its progress as it runs. If the server crashes, it picks up from the last completed step instead of starting over. Traditional server code loses everything on restart. Durable workflows don't. Think of it like a save point in a game. That makes them the most stable way to run AI systems or anything that needs to wait, for an API response, a human approval, or a scheduled delay, and still finish reliably. For AI orchestration specifically, where you're chaining multiple model calls, tool lookups, and external APIs together, durability turns a fragile chain into one that finishes even when a step fails. ### Do we have to use Vercel? No. eve and the Workflow Development Kit are both open source, and the AI SDK is portable across model providers, so the core runs on AWS, Google Cloud, or your own servers. Vercel gives you zero-config deployment, managed Sandbox for code execution, and Connect for runtime credentials, which is why we default to it. The architecture travels; the managed convenience is what you'd trade away by self-hosting. ### Can you connect to our existing tools? If it has an API, we can wire it in. We've built integrations with Ahrefs, PostHog, Slack, Sanity, various CRMs, payment processors, and custom internal tools. Each integration is a step in a workflow, so it gets automatic retries and error handling for free. ### What does 'agentic' mean here? Software that acts on its own with human oversight. An agentic workflow might research a lead, draft a blog post, or classify a support ticket without anyone clicking a button. A human reviews the output before it goes live. The agent handles the grunt work, people handle the judgment. ### How do you handle AI accuracy? Every workflow we build has a human review step where it matters. AI drafts the blog post, your editor approves it. AI enriches the lead, your sales rep reads the brief. AI classifies the ticket, your support team sees the suggestion. We don't ship workflows where AI output goes straight to your customers without a check. We also build eval pipelines for every agent we put into production, so quality degradation gets caught before your team feels it. ### We already have AI features. Can you improve what we have? Most teams we work with have a working prototype that needs to become production-grade. That usually means adding durability so it doesn't break on deploy, observability so you can debug failures, evals so quality stays measurable, and proper error handling so one bad API response doesn't tank the pipeline. We audit what you have and figure out the fastest path to reliable. ### How do you keep token costs under control? By treating it as a build decision rather than a cleanup job. Each step in a workflow runs on the model that suits it, so classification, extraction and routing sit on a fast, cheap model and only the steps that need real judgment reach for the expensive one. The AI SDK is provider-agnostic, so moving a step onto a different model is a config change instead of a rewrite. We cache prompts where the same context repeats, and give each step only the context it needs rather than passing the whole conversation around. Every run reports its own cost into PostHog LLM analytics from the first deploy, so you can see which step got expensive instead of guessing. If you already have an agent live, we can start there: instrument it, find where the tokens actually go, and bring them down with the eval suite catching any quality drop on the way. ## Put an agent to work on your busywork The content backlog, the unenriched leads, the catalogue nobody can keep current. Pick the one costing your team the most and we'll scope it. From the blog --- ## Astro website development > Astro development agency for content-heavy websites. We ship static-fast pages with islands for the interactive parts, wired to Sanity or Contentful, and we'll tell you honestly when Next.js is the better fit. **Updated:** 2026-08-25 --- Companies of all sizes trust Roboto Studio Most content sites ship an application framework's worth of JavaScript to render pages that are, honestly, documents. Astro flips that default. Every page renders to plain HTML on the server, and JavaScript is opt-in per component: the search box hydrates, the ten paragraphs around it don't. Your visitors get pages that load instantly on hotel wifi, and your Core Web Vitals stop being a quarterly remediation project. We build Astro sites the same way we build everything else: structured content first. Your pages come from Sanity, Contentful, or MDX collections in your own repository, so editors keep a real workflow and nothing about the frontend is welded to the content. When a page needs genuine interactivity, we drop in an island built with React, and the rest of the site stays static. Astro is our recommendation when the site is overwhelmingly content: marketing sites, documentation, editorial publications, blogs that have outgrown their CMS's frontend. It is not our recommendation for logged-in products, dashboards, or sites where personalisation drives every page, because that's application territory and [Next.js](/services/nextjs) handles it better. You shouldn't have to arbitrate a framework debate to buy a website. Next.js is our daily driver and Astro shares its content-first playbook, so we hold no framework loyalty and we'll tell you in the first call which one your requirements actually point to. Quite often the Astro answer is also the cheaper one to build and the cheaper one to run. Content-first builds we've shipped ## Considering Astro? The questions we get asked when teams are weighing Astro against an app framework. ### What is Astro and when is it the right choice? Astro is a web framework built for content-heavy sites: marketing pages, blogs, documentation, editorial publications. It renders everything to static HTML by default and only ships JavaScript for the components you explicitly mark as interactive. If most of your site is content and a handful of widgets, Astro is usually the right call. If your site is mostly application, it isn't. ### Astro or Next.js, which should we pick? Our honest rule: [Next.js](/services/nextjs) when the site has real application logic, personalization, or heavy interactive surfaces; Astro when the site is overwhelmingly content with a few interactive islands. Next.js is what we ship week in, week out, and Astro shares its content-first playbook, so the recommendation follows your requirements rather than our preference. We'll tell you which one fits in the first call, and it's often the less expensive answer. ### Does Astro work with a headless CMS? Yes, and that's how we build it. Astro pairs cleanly with [Sanity](/services/sanity) and [Contentful](/services/contentful): your editors keep a real editorial workflow, and the site rebuilds or revalidates when content changes. Content collections also make on-disk MDX a first-class option for teams who want content in the repo. ### What are islands and why do they matter? An island is a single interactive component, like a search box or a pricing calculator, hydrated on an otherwise static page. Instead of shipping a whole application bundle to render mostly static content, Astro ships JavaScript only for those islands. The result is less code over the wire and Core Web Vitals that hold up under real traffic. ### How does Astro help SEO and AI search? Every page is fully rendered HTML with no client-side rendering gap, which is exactly what search crawlers and answer engines like ChatGPT and Perplexity want to read. Combined with fast load times and structured data baked into the templates, Astro sites are easy to index and easy to cite. That's the same crawlability work behind [our AI SEO service](/services/geo). ### Can you migrate our existing site to Astro? Yes. An Astro migration follows the same URL-map and 301 playbook as [every CMS replatform we run](/services/cms-migration): each legacy URL mapped, rankings carried over, and your content brought along or remodeled as part of the move, whether it currently lives in WordPress, Webflow, or a legacy framework. ### Can Astro handle interactive features and apps? Within reason. Islands can be React, Svelte, or Vue components, so carts, search, filtering, and forms are all comfortable territory. When the interactive surface grows into a genuine application, dashboards, logged-in product, complex state, we'll steer you to Next.js instead of stretching Astro past what it's good at. ### Who maintains the site after launch? Either your team or ours. Astro sites are deliberately simple to run: content edits go through the CMS, and the codebase is small enough for one developer to hold in their head. If you'd rather not staff it, [our retainer](/services/sanity-support) covers upgrades, new sections, and incident response. From the blog **Tell us what you're publishing.** A marketing site that's outgrown its builder, docs that need to load instantly, or a content platform where the app framework is doing nothing but slowing you down: we'll scope it in a 20-minute call and tell you whether Astro fits, including when the answer is Next.js instead. --- ## Brand design > Brand design from the studio that also builds the website. We design identity systems, typography, and motion that ship as design tokens and components your site uses on day one, not a brand book that gathers dust. **Updated:** 2026-08-25 --- Companies of all sizes trust Roboto Studio The standard brand handover is a hundred-page PDF and a folder of logo exports. Then a developer squints at it, picks the nearest hex code, and the erosion starts: the website drifts from the deck, the deck drifts from the product, until nobody can say what the brand actually looks like. We design brands the way we build websites: as systems. Identity decisions become design tokens, typography becomes a type scale your CSS consumes, and components carry the brand into every page automatically. The guidelines still exist, but they're generated from the working system rather than hoping someone reads them. Approve a colour once and it renders that way everywhere, from your homepage to your OG images. Brand agencies design; development agencies interpret. The gap between those two is where identity work goes to die. Because we do both, your brand runs a few weeks ahead of the website build: identity lands as tokens, tokens land in components, and the site launches as the first full expression of the brand rather than a translation of it. That also means the brand is designed for where it will actually live. Motion principles that respect performance budgets, colour systems that hold up in light and dark mode, typography chosen for how it renders on screen and not just how it looks on a poster. If you only need the identity work, that's fine too; everything we hand over is built so any competent team can implement it. Design-led builds we've shipped ## Thinking about your brand? The questions we get asked when teams are weighing a refresh against a rebrand. ### What does your brand design service cover? Identity design (logo, typography, color), a design system that encodes it as reusable components and tokens, the assets a launch actually needs (social, OG images, pitch templates), and motion principles for how the brand behaves on screen. The deliverable is a working system, with the traditional brand guidelines generated from it rather than the other way round. ### Why buy brand design from a development studio? Because the most common way a brand dies is translation. An agency hands over a beautiful PDF, a developer approximates it in CSS, and soon the website, the deck, and the product all disagree. When the same studio designs the identity and builds the site, the brand ships as design tokens and components, so what you approved is what renders. ### Do we need a full rebrand? Usually not, and we'll say so. Most teams need a refresh: keep the recognizable core, fix the typography, systematize the color, and build the component library that makes the brand consistent everywhere. A full rebrand is for genuine repositioning. We'll tell you which one you're actually shopping for in the first call. ### What do we get at handover? Figma libraries wired with variables, design tokens your developers consume directly, a component library if we're building the site too, brand guidelines as a living document, and the launch asset kit. Everything is versioned and editable, so the system keeps working after we step away. ### Can you work with our existing brand? Yes. A lot of our brand work starts from an identity that's fine but has never been systematized: the logo is good, everything else is improvised per project. We audit what exists, keep what works, and encode it into a system your team and your website can apply consistently. ### How does brand design pair with a website build? Naturally, and it's the pairing we recommend. Brand runs a few weeks ahead of the build: identity decisions land as tokens, tokens land in components, and the site becomes the first full expression of the brand instead of an approximation of it. One team, no translation loss, one timeline. See how we build on [Sanity](/services/sanity) and [Next.js](/services/nextjs). ### How long does a brand project take? A systematization of an existing identity typically runs three to four weeks. A new identity with a full design system typically runs six to eight, scoped per engagement. Paired with a website build, the brand work fronts the timeline so development never waits on design decisions. **Tell us where the drift hurts.** A launch that needs an identity, a good logo trapped in an inconsistent system, or a rebrand you want built into the website rather than translated onto it: we'll scope it honestly in a 20-minute call. --- ## CMS migration agency > CMS migration agency for teams replatforming off WordPress, Webflow, HubSpot, Drupal, Sitecore or Framer. We move content, preserve URLs, and keep search rankings intact so you don't lose 30% of your traffic the week after launch. **Updated:** 2026-08-25 --- ## Move CMS without losing your traffic ### We replatform WordPress, Webflow, HubSpot CMS, Drupal, Sitecore and Framer sites onto Sanity or Contentful. URLs preserved. JSON-LD parity. Redirect map signed off before cutover. Editors trained before, not after. The week after launch is where most migrations get judged, and most agencies have already invoiced and moved on. We stay through the post-launch monitoring window, watch the GSC and Ahrefs numbers daily, and fix the things that always surface once Googlebot starts re-crawling. Migrations we've shipped for ## The SEO migration agency that closes the loop ### We've moved hundreds of thousands of pages between CMS platforms over the last six years. Every migration has the same risk profile: get the redirects, sitemaps and structured data wrong and you watch months of compounding organic traffic disappear inside a fortnight. Our process is built around the parts that quietly cost teams their rankings: the redirect map that gets signed off in a spreadsheet before any code ships, the JSON-LD parity audit that confirms every schema type on the old site exists on the new one, the staged cutover that lets us roll back without anyone outside the team noticing, and the 30-day post-launch monitoring window where we catch the regressions Google surfaces a fortnight in. ### We've been pulled in to recover migrations where the new site shipped clean, looked great, and lost 30 to 70% of organic traffic in the first month. The pattern is always one of four root causes. **Broken redirect maps.** The team mapped the top 100 URLs, missed the 4,000 long-tail pages with three backlinks each, and surrendered all of that link equity to a soft 404. **JSON-LD that quietly went missing.** The old WordPress site had Article, FAQPage, BreadcrumbList and Organization schema on every post. The new build shipped with none of it, and the rich results disappeared from the SERP within a fortnight. **Sitemap regressions.** The new sitemap had 600 URLs, the old one had 4,800. Google noticed before anyone on the team did. **Image and OG drift.** Image filenames changed, alt text was dropped, Open Graph images defaulted to a generic logo. Social previews went blank and image search traffic evaporated. The good news is every one of these is preventable with a checklist run before, during and after cutover. We've published our full version of that checklist as a [pre-launch essentials guide](/blog/the-pre-launch-essentials-checklist-for-cms-migrations), which we run on every migration we ship. Migration is where most teams find us, but plenty of our work is building headless CMS sites from scratch. As a [headless CMS agency](/services/headless-cms) we model the content, build the Sanity or Contentful backend, and ship a Next.js frontend on top, with the editorial experience designed around how your team actually publishes. If you are weighing up a headless CMS development company for a greenfield build, the standard is the same as our migration work: a content model that survives new page types, preview your editors trust, structured data and performance handled properly, and a frontend your developers can keep extending long after launch. The [headless CMS service page](/services/headless-cms) covers the greenfield offer in full. Our six-stage migration process We snapshot the current site before anything moves. Full Ahrefs export of ranking keywords and referring domains, GSC performance baseline across the last 12 months, sitemap diff, internal-link graph, and a list of every JSON-LD schema type currently in use. This is the document we measure success against post-launch. We design the target schema in Sanity or Contentful around how your editors actually work, not the shape of the legacy database. Page builders, reusable references, validation rules and roles get scoped before any data moves so the new model still fits your team six page-type requests from now. Every legacy URL gets mapped to a destination. Categories, tag archives, author pages, paginated lists, the long tail of orphaned posts: nothing gets surrendered to a soft 404. The map is reviewed, signed off, then implemented as proper 301s at the edge so search engines see permanent moves. We catalogue every structured-data type on the old site and rebuild it on the new one. Article, FAQPage, BreadcrumbList, Product, Organization, VideoObject: each schema type gets parity, validated through the Rich Results Test before launch and re-validated after. We launch behind a feature flag or staging domain with the redirect map already in place. Real traffic gets a controlled rollout, the new sitemap gets submitted at the same moment as the DNS flip, and the old site stays warm in case we need to roll back. Most clients see zero downtime through cutover. The 30 days after launch are where rankings actually settle. We monitor GSC coverage, indexing, Core Web Vitals and rank tracking daily for the first week, weekly through the first month. Every regression gets investigated and fixed before it compounds. We've documented the exact pre-launch checks we run on every migration: redirect-map QA, sitemap audit, JSON-LD parity, OG image audit, robots.txt and llms.txt review, Ahrefs and GSC baseline capture, internal-link audit, image migration to Vercel Blob, performance regression check, and the post-launch monitoring cadence. Tick through the interactive version below as you work the migration. Your progress is saved in your browser, so you can step away and come back to it across the weeks the migration actually takes. Moving from your current CMS Migration builds we've shipped ## Thinking about a CMS migration? The questions we get asked on every migration scoping call. ### Are you a headless CMS agency? Yes. We build and migrate headless CMS sites on Sanity and Contentful, with a Next.js frontend on top. Whether you're starting fresh or moving off WordPress, Webflow, HubSpot, Drupal or Sitecore, we handle the content modeling, the build, the redirect map and the post-launch monitoring. Our [headless CMS agency page](/services/headless-cms) covers the greenfield side; this page covers getting the migration right. ### How long does a CMS migration take? Most mid-market migrations take 6 to 12 weeks end-to-end, depending on content volume, the complexity of the source schema, and how much content modeling work the new platform needs. Enterprise migrations with multi-language sites and hundreds of thousands of entries run longer. We scope honestly on a discovery call and break the timeline into audit, build, content move and post-launch monitoring stages so progress is visible week by week. ### Will we lose SEO traffic during a CMS migration? Not if the redirect map, sitemaps and structured data are handled properly. Every URL with organic traffic or backlinks gets a 301 to its closest equivalent on the new site, the new sitemap gets submitted to GSC on cutover day, and JSON-LD schema gets rebuilt with parity to the old site. We've shipped migrations where organic traffic was flat through cutover and growing within four weeks. The reverse is also true: skip the redirect map and you'll watch months of compounding traffic disappear inside a fortnight. ### What does a CMS migration cost? The honest answer: it depends on content volume and how much modeling work the new platform needs. A 5-page brochure site moves for a few thousand pounds. A 4,000-page enterprise site with multi-language content, a custom redirect map and JSON-LD parity work is a multi-month engagement. We scope every migration in two stages: a paid audit that surfaces every risk, then a fixed-fee build against the audit findings. No surprises mid-project. ### Which CMS should we migrate to? Most teams we work with land on Sanity, because the structured content model, real-time collaboration and Live Content API fit modern editorial workflows better than anything else on the market. Contentful is the right call for enterprise teams with existing Contentful licenses, complex role-based publishing or multi-region content operations. Where the choice matters, we'll walk you through both on a scoping call instead of pretending one fits everyone. ### Can you migrate from WordPress to Sanity? Yes. WordPress to Sanity is the most common migration we run. The pattern is well-trodden: export WordPress content via the REST API or XML, map post types, categories and ACF fields to a Sanity schema, run the redirect plan across slugs that almost never line up perfectly, and rebuild the JSON-LD schema your old Yoast or Rank Math setup was emitting. We've documented the technical walk-through in our [how to migrate from WordPress to Sanity fast](/blog/how-to-migrate-from-wordpress-to-sanity-fast) guide. ### Can you migrate from Webflow to Sanity? Yes. Webflow to Sanity is our second-most-common migration. The Webflow CMS export gives us the source data; the structural work is in mapping Webflow Collections to a Sanity schema that actually scales past the platform's hard limits on collection size and item count. Rich text fields and component references need careful handling. We've run this pattern on sites with over a thousand pages without losing visible search rankings through cutover. ### What about HubSpot CMS, Drupal or Sitecore? All three are migrations we run. HubSpot CMS migrations typically head to Contentful, because clients on HubSpot are usually already paying for an enterprise stack and want the editorial parity. Drupal and Sitecore migrations are enterprise engagements: deeper content modeling, more careful URL planning, longer monitoring windows. Talk to us on a scoping call and we'll walk through the source-specific risks before quoting. ### What's the worst-case scenario if a migration goes wrong? The cautionary tale we keep referencing: a team replatformed without a proper redirect map and watched organic traffic drop to roughly a third of pre-launch volume inside three weeks. Recovering that is months of work, and some of the lost ranking never comes back because the new pages don't have the same backlink profile the old URLs did. The pre-launch essentials checklist exists because every step on it has, at some point, caught a problem on a real engagement. **Tell us what you're moving off and where you'd like to land.** We'll send back a fixed-fee audit proposal that prices the migration honestly, surfaces the risks before contracts get signed, and gives you a redirect-map sample so you can see how we work before you commit to a build. From the blog --- ## Contentful development agency > Contentful development agency for enterprise content teams. We model content, migrate from WordPress and HubSpot CMS, run A/B tests in Contentful and ship integrations your marketing team actually uses. **Updated:** 2026-08-25 --- ## Contentful development agency ### The Contentful agency for teams shipping content at scale. We build, migrate and integrate, from multi-language storefronts to enterprise sites with hundreds of thousands of structured entries. We've shipped Contentful at every scale, from a first build to estates with hundreds of thousands of entries. The demo is never the hard part. What counts comes later: a content model that still makes sense at the tenth content type, a migration that keeps your rankings intact, an integration that survives the next webhook change. That middle stretch is the work we're known for. We handle the modelling, the migration and the integration plumbing, so editors and developers both get a Contentful they can live with as content types and locales multiply. Companies of all sizes trust Roboto Studio ## Contentful specialists, not Contentful tourists ### We build Contentful sites from scratch, and we get called in to fix the ones that drifted. Doing both is how you learn to keep it clean. Most agencies treat Contentful as a black box. We don't. A page builder needs real architecture, so a single edit changes one page and not forty. Content types need scoping early, so your bill stays predictable as the site grows. Migrations from WordPress, Drupal and HubSpot CMS get the same care with redirects and search equity that we'd put into a rebuild we couldn't afford to get wrong. When a Contentful site has lost performance or organic traffic, teams bring us in to rebuild the foundations without a full re-platform. When you're starting fresh, we'd rather get those foundations right the first time. Either way, as a Contentful Silver Solution Partner our Contentful developers work alongside your editors and engineers so the platform keeps pace as the content estate and the team grow. Editors work side-by-side in Contentful with real-time collaboration, scheduled releases and clear approval flows. We design the content model and roles so marketing, product and legal can work in the same space without overwriting each other's changes. Editors see exactly how content will look on any device or channel before it goes live. We wire up Contentful's preview workflows into your Next.js or Astro frontend so marketing can move fast without "please redeploy" tickets. Contentful's built-in AI speeds up drafting, translation and on-brand variant generation. We help editorial teams fold it into how they already work rather than treat it as a separate tool to learn. We handle the unglamorous middle of a migration: mapping legacy content, redesigning the schema, preserving URLs and SEO equity, and getting editors trained before the cutover. We've done it for HubSpot CMS, WordPress and homegrown databases. Run A/B tests and audience-targeted variants directly inside Contentful, without bolting on a separate experimentation tool. We set up the rules, the analytics wiring and the editorial guardrails so your team can iterate without engineering hand-holding. HubSpot forms, Salesforce, Marketo, Algolia, Mux. We wire Contentful into the rest of your stack so data flows both ways and keeps flowing the next time someone changes a webhook. When a Contentful site slides in search, the cause is usually in the build. Landing pages stitched together from references bloat the rendered HTML. The frontend paints slowly under real traffic. Meta titles, descriptions and structured data never made it into the content model, so editors publish pages that can't describe themselves to a crawler. Every new entry inherits the same problems, and the slide compounds. We fix the foundations without forcing a re-platform. That means auditing the rendering path and cutting the dead weight out of every template, moving SEO fields and schema markup into the content model itself so a page can't ship without them, and rebuilding the redirect map so the equity from old URLs lands where it should. The audit tells us which fixes pay back first, and the work runs in that order. The same foundations go into every Contentful site we build from scratch, because recovering rankings costs far more than never losing them. Ranking in Google is also only half the picture now: our [generative engine optimisation](/services/geo) work covers how your site shows up in ChatGPT, Perplexity and AI Overviews. Contentful acquired Ninetailed and built it into the platform as Contentful Personalization, which changes what an experiment costs to run. Audience segmentation, A/B and A/B/n tests at the component level, and the insights to read the results all live where your editors already work, with no separate experimentation tool to license, integrate and keep in sync with your content. Our job is the implementation that makes it trustworthy. We define audience segments against real visitor data rather than guesses, wire experiment results into the analytics you already report from, and set the editorial guardrails so a variant edit never silently changes the live entry. That last part matters more than it sounds: variants ride on the same reference system that powers the rest of Contentful, and the same architecture discipline we apply to page builders applies here. Once that's in place, marketing runs the programme on their own. A new hero variant for returning visitors is an afternoon's work in the editor rather than an engineering ticket. The experiment ships, the numbers come back, and the winner gets promoted without anyone touching code. ## Silver-tier, with production builds to match Contentful made us a Silver Solution Partner because we keep shipping. Our Certified Professionals are deep in composable commerce builds today, and most weeks one of them is wrestling a WordPress or HubSpot CMS migration into a clean, multi-brand Contentful setup. Jono and his team are absolute rockstars. They blend technical savvy with practical business sense. They are in lock-step with our website goals and have really made our website come to life. Not just a web dev team, they are trusted advisors and truly aligned with our team. Contentful builds we've shipped Which Contentful plan you actually need ## Thinking about building with Contentful? The questions we get asked most often ### What is Contentful? Contentful is an API-first headless CMS. Content lives in the cloud as structured entries, and any frontend (a Next.js site, a mobile app, a digital sign) pulls it in via REST or GraphQL. The trade-off versus a traditional CMS is more upfront content modeling in exchange for far more flexibility on how and where you publish. ### Is Contentful a headless CMS? Yes. Contentful was one of the original headless CMSes and is fully decoupled from any frontend. You model content in Contentful, then deliver it through REST or GraphQL APIs to whatever you're building. ### What does a Contentful agency do? The parts of a Contentful build that aren't frontend code: content modeling, migration planning, integration plumbing, editorial workflow design, and the governance that stops a page builder decaying edit by edit. A good Contentful agency will also tell you when Contentful is the wrong choice for your content model. Most of our clients keep the frontend and product roadmap in-house and bring us in for the platform work that has to be right the first time. ### Is Roboto Studio a Contentful partner? Yes, we're a Contentful Silver Solution Partner with Certified Professionals on the team. Partner status gives us a direct line into Contentful and early sight of platform changes, which is how our Contentful developers tend to have already shipped the feature you're about to adopt. ### How does Contentful pricing work? Contentful publishes its pricing openly: a free tier for small teams, then Lite, Premium and Enterprise plans priced by users, content types, locales and API call volume. Most mid-market teams land on Premium; enterprise plans add SLAs, SSO and custom roles. The honest answer most agencies don't give you: budget for the implementation and content-modeling work too, because the license is usually a small fraction of total cost in year one. ### How do we stop our Contentful bill from escalating as we add features? Most pricing surprises in Contentful trace back to a content model that grew without a plan. Every new editorial idea gets its own content type, locales multiply, and suddenly you're bumping the tier ceiling. We scope content types deliberately on day one, reusing fields and leaning on references where they belong, so you keep room to grow inside the plan you already pay for. ### When would you recommend something other than Contentful? When content needs to nest six or seven layers deep, or when editors expect a universal block library reused across many page types, Contentful starts to fight you. In those cases we'll usually recommend Sanity, which handles nested structures and reusable blocks more gracefully. We'd rather lose a project at the proposal stage than watch you fight your CMS on every new page type. Picking the wrong tool is a much more expensive mistake than picking a different agency. ### How do you build a Contentful page builder that stays maintainable? Contentful's reference system is powerful and easy to misuse. A naive page builder ends up with shared blocks referenced across dozens of pages, so a single edit silently changes content everywhere it appears. We design page builders with clear separation between reusable components and page-scoped content, then add editorial conventions and roles so contributors always know which edits are global. The architecture is what makes the difference between a flexible system and a quiet bug factory. ### Can Contentful handle multi-language sites? Yes, localization is one of Contentful's strongest areas. You define locales centrally, then translate per-field with fallbacks and per-locale publishing. We've shipped Contentful into multi-language storefronts and editorial sites where the same content team manages a dozen markets without each one diverging into its own bespoke build. ### Can you migrate from WordPress, Drupal or HubSpot CMS to Contentful? Yes, we run these migrations regularly. We map your existing content, design the new content model, preserve URLs and SEO equity with 301s, and migrate editors as carefully as we migrate data. HubSpot CMS and WordPress are our most common sources. ### What access do you need from us to run a Contentful migration? For the source CMS, we usually need an admin or export-level role so we can pull every entry, asset, taxonomy and redirect. Read-only API access works for most platforms, though WordPress migrations occasionally need database or filesystem access for complex post types. On the Contentful side, an Owner or Admin role on the space is enough for us to create the content model, run the migration scripts and configure roles. We can scope the access tightly and tear it down the day we hand over. ### When migrating articles, should the body be one rich-text block or many? Both are valid, and the right answer depends on what your editors actually need. A single rich-text body is simpler to migrate and edit, though it limits what you can do with embedded components, experiments inside an article, or analytics on individual sections. Splitting the body into multiple typed blocks (hero, rich text, quote, CTA, and so on) gives editors and marketers far more flexibility, but takes more migration logic to map old HTML cleanly. For article templates that need to keep evolving, the block approach almost always wins out. ### Our Contentful site is losing rankings, can you help? Yes, this is one of the most common reasons teams bring us in. Contentful sites tend to regress in search when the frontend is slow, when landing pages are stitched together from references that bloat the rendered HTML, or when metadata and structured data weren't part of the original content model. We audit the build, fix the underlying causes, and rebuild the content model, frontend performance and schema markup without forcing a re-platform. ### Can you run A/B tests in Contentful? Yes. Contentful supports A/B testing and audience-targeted content variants natively, and we wire up the editorial workflow, the analytics tagging and the guardrails so your marketing team can run experiments without an engineering ticket per test. Plenty of our Contentful work starts on someone else's platform. We move teams over from enterprise systems like Adobe Experience Manager and Sitecore, from WordPress and Strapi, and from other headless tools including Sanity and Storyblok. Each plan below covers the content modelling, the URL and 301 redirect work, the editor training and the cutover, so you can see what the move actually involves before committing to it. --- ## Generative engine optimization > AI SEO, GEO, and AEO under one roof. Background agents monitor your citations across ChatGPT, Claude, Perplexity, and AI Overviews, draft the fixes, and ship them under your approval. **Updated:** 2026-08-25 --- A cynical reader might suspect we are keyword stuffing in this section... But whether you're looking for an AI SEO agency, AI SEO services, generative engine optimization agency, generative engine optimization services, AI SEO company, answer engine optimization. _They're all the same god-damn thing_ and they almost all hinge around the same core principles. > Create well marked-up content, iterate on it faster, and make it easier for AI models to read and understand. AI SEO is the plain-language term. GEO (generative engine optimization) and AEO (answer engine optimization) are the technical ones, and we use them interchangeably. Whichever acronym you arrived with, the deliverable is identical: find where ChatGPT, Claude, Perplexity, and Google's AI Overviews pull their answers, ship the changes that put you inside those answers, and measure whether your citation share moved. The rest of this page is how we run that loop and more importantly **how fast**. What most GEO gets wrong Most GEO content published today is taxonomy work: "What is GEO?", "GEO vs SEO". LLMs already have this, and it competes with Wikipedia and five hundred near-identical posts. The citation wins live in specific, evidence-backed claims that can't be scraped from a dictionary. llms.txt and schema both help. Profound and Vercel publish evidence for llms.txt, and structured data improves crawlability and context. Each is one ingredient. Ship a 30-line llms.txt file or a schema pass on its own, leave content negotiation untouched, and you have optimised the wrong layer. Volume without editorial infrastructure is AI slop. LLMs weight source trust, so a corpus full of AI tells and unverified claims trains the wrong signal. More content behind no quality bar can move citation share in the wrong direction. A monthly content brief delivered as a PDF is not GEO work. The diagnostic-to-ship gap in advisory retainers is measured in quarters. GEO needs a closed feedback loop: detect a citation shift, draft a remediation, ship it, re-monitor. That loop can't run on a monthly report cadence. How we do it Per Ramp's first-party research, markdown was the only format that reliably surfaced in LLM responses. Bots ask for markdown. Browsers get HTML. The same URL can serve both. That is content negotiation, and instrumenting it per page is the single change with the widest citation coverage. Roboto's own site is the worked example. The content is MDX on disk, so the markdown is the source rather than a format derived from a CMS export. When an LLM crawler asks for markdown at a given URL, it gets a clean document with structured headings, inline links, and no CMS wrapper noise. We wrote up the approach in [our Next.js AEO/GEO guide](/blog/nextjs-aeo-geo). On a CMS-driven stack it's a transform step rather than a route handler, but the goal is the same clean markdown at the canonical URL. Quarterly advisory cycles break at the feedback step. You get a report in month three describing what happened in month one, and by the time an edit clears approval and publish, the competitive set has shifted again. The diagnostic is stale before the fix ships. A [background agent](/services/agentic-workflows#background-agents) watches citation share across platforms on a weekly cadence. When share drops on a target prompt cluster, it flags the change, identifies what moved in the competitive set, and drafts the fix: a content edit, a schema patch, an llms.txt update, or a content negotiation rule. One of our engineers reviews it and ships it, then the agent re-monitors that cluster and measures whether share recovered. We run this on our own site and documented the build in [why we built our own background agent](/blog/why-we-built-our-own-background-agent). For an engagement we set the same loop up on your stack: your data sources, your content repo, your approval flow. Bot classification is where most implementations break. Single-signal classification on User-Agent alone misses the bulk of AI crawler traffic. Correct classification combines UA, IP range, ASN, and bot score. The Cloudflare gotcha compounds it: ChatGPT, Perplexity, and Claude bots are categorised as "AI Assistants" in Cloudflare's taxonomy, not "AI Search", so firewall and analytics rules targeting only "AI Search" miss all three major platforms. The rest is table stakes we verify before touching anything higher up. Schema goes in where it helps LLMs parse structure (ServiceSchema, FAQPage, BreadcrumbList, Article). The llms.txt file gets prioritisation logic rather than a flat list of 400 pages. Crawlability is genuinely non-negotiable: every list item rendered into the DOM, navigation in static HTML, correct status codes, and clean submitted sitemaps. Volume compounds citation share only when the content clears a quality bar that LLMs trust. The editorial infrastructure is what makes volume safe to ship, so we build three gates into the publishing pipeline. An automated check runs before merge, flagging em-dashes, negative parallelism, and the vocabulary we've caught models overusing; content that fails goes back to be rewritten. A fact-checker agent verifies specific claims against their cited primary sources and flags any that don't hold. A final editing pass strips the sentence structures that read as generated. The cms-auto-updater pipeline is the worked example: it pulls evidence from primary sources on a weekly cadence, drafts updates, runs all three gates, and queues each change for human sign-off before it publishes. The result is far more content per quarter without the citation-eroding slop that comes from raw AI publish pipelines. How we deliver Fixed-scope review of your current citation footprint and technical readiness. Covers content negotiation, bot classification, schema, llms.txt, and crawlability, plus competitor citation analysis across target prompt clusters and a prioritised remediation roadmap ordered by expected impact. Duration: 1 to 2 weeks. Deliverable: written report and walkthrough call. Implementation engagement. We wire up content negotiation across key pages, fix bot classification, implement llms.txt with prioritisation logic, add schema where missing, and restructure high-value content for citation extraction. Monitoring dashboards for citation share, crawler activity, and AI-referral traffic come standard. Duration: 4 to 6 weeks typical, scoped per engagement. Everything above running continuously on your stack. Weekly agentic monitoring across citation, crawler, and search data sources. Agent-drafted fixes reviewed and shipped by Roboto. Monthly synthesis call covering what moved, what we shipped, and what's queued. Re-baselined quarterly. Minimum 3-month commitment. Part of this retainer is a weekly agent that audits the analytics platforms you already use, condenses them into one place, and hands your team the next steps to take. It does the reading and the triage so the monthly call is about decisions, not dashboards. When a community thread outranks your own page, that thread is an opportunity. This bot finds those threads for your keywords and drafts a response built from your own content, so you get a second touch point on a page that already wins the search. ## The skeptic's FAQ What people actually want to know about GEO ### Does llms.txt actually do anything? It helps, and Profound and Vercel publish positive evidence on it. Ramp's first-party experiment notably didn't recommend it as a lead tactic. Treat llms.txt as one layer alongside content negotiation, schema, and crawlability, not a silver bullet. ### Is AI SEO the same as GEO and AEO? Yes. AI SEO is the buyer-facing label. GEO (generative engine optimization) and AEO (answer engine optimization) are the technical names for the same work: getting cited inside AI answers rather than ranking blue links. We use the terms interchangeably. Anyone selling them as three separate products is inventing scope. ### Is GEO real or just SEO with a new label? The substrate is different. SEO optimizes for ranked search results. GEO optimizes for citation inside an LLM response. The technical work overlaps (crawlability, schema, content quality) but the diagnostic loop is new: which prompts cite you, which models, on which platforms, and how that share moves week to week. Selling an SEO retainer as a GEO retainer is the grift. ### How fast can citations actually move? Faster than organic rankings, slower than paid ads. A substrate change like content negotiation, a schema patch, or a content rewrite can move citation share within weeks rather than the quarters an advisory retainer takes to close the loop. How fast depends on your starting point and how competitive your target prompt clusters are. ### Why isn't publishing more content the answer? Volume without editorial gates and fact-checking compounds AI slop. Doubling publishing cadence without those gates risks moving citation share the wrong way, because LLMs deprioritize low-trust sources. The win is volume with infrastructure: checks that catch AI-sounding phrasing before publish, fact-checker agents that verify claims against primary sources, and an editing pass that strips the expressions we've caught models overusing. ### Do I need GEO if I already do SEO? If your buyers use ChatGPT, Claude, Perplexity, or Google AI Overviews to evaluate vendors, yes. The first-party data shows AI search is now a meaningful share of the discovery funnel. If your buyers still arrive exclusively via blue-link Google, no, but that's a shrinking population, especially in B2B. Recent builds we've shipped The research behind it The most specific public dataset on AI citation dynamics to date. Reddit citation share roughly doubled October 2025 to January 2026. Perplexity attributes 31% of all citations to social media, with Reddit at 24%. AI Overviews citations dropped 46% in the same period. The implication: social platforms are now a primary citation surface, and tracking only your own domain misses where citations originate. [Source: tryprofound.com](https://www.tryprofound.com) First-party experiment on what formats LLMs actually surface. Markdown was the only format that reliably appeared in responses. Bot classification requires combining UA, IP, ASN, and bot score, since single-signal approaches miss the major platforms. llms.txt was not recommended as a lead tactic in their findings. [Source: builders.ramp.com](https://builders.ramp.com) A playbook for distinguishing real LLM crawlers from spoofed user agents via server log analysis. The spoofing rate for AI crawlers is material, so acting on UA alone gives a distorted picture of which platforms are actually crawling your content and how often. Server logs are the ground truth. [Source: merj.com](https://merj.com) First-party data from Vercel's CDN on AI crawler behaviour, llms.txt adoption and impact, and framework-level responses to AI traffic. Their data shows a positive correlation between llms.txt presence and citation rate, alongside the content negotiation patterns that improve LLM ingestion. [Source: vercel.com/blog](https://vercel.com/blog) Mike King's technical research on the factors that correlate with LLM citation: content structure, entity coverage, E-E-A-T signals as proxied by LLMs, and the structural differences between content that gets cited and content that doesn't. One of the more rigorous public treatments of GEO. [Source: ipullrank.com](https://ipullrank.com) Research on how AI Overviews affect click-through rates on organic results, and the crossover between pages that rank organically and pages that get cited in AI responses. The data suggests high overlap at the top end, so strong organic pages stay relevant while citation remains a separate signal worth tracking on its own. [Source: ahrefs.com/blog](https://ahrefs.com/blog/) From the blog ## Ready to find out if AI engines cite you? If your buyers research vendors through ChatGPT, Claude, Perplexity, or Google AI Overviews and you don't know whether you show up, a discovery call is the fastest way to find out. We'll show you the citation gap and walk you through the closed-loop fix. --- ## Headless CMS agency > Headless CMS development agency for content teams operating at scale. We model structured content, build on Sanity and Contentful with Next.js frontends, and run migrations that keep your URLs and rankings intact. **Updated:** 2026-08-25 --- ## The headless CMS agency that specializes on purpose ### Most headless CMS agencies list eight platform partnerships and call it flexibility. We build on Sanity and Contentful, because depth beats a badge wall: one of 15 starred partners in Sanity's 218-agency directory, Contentful Silver Solution Partner, and a 550-page migration matrix covering the platforms we've moved teams off. Structured content is the whole job. We model your content around how your editors work, ship the CMS and the Next.js frontend, and stay for the unglamorous part: the schema that still makes sense at the tenth content type, the integration that survives the next webhook change, the migration that keeps your rankings. If your content estate would be better served by something we don't build, we'll tell you on the first call. The difference between a headless CMS your editors love and one they file tickets about is the content model. We design schemas around editorial workflows, with reusable blocks, sensible references and validation that catches mistakes before publish. Sanity or Contentful, and occasionally neither. We hold partner status with both platforms, so the recommendation follows your content model, your team and your budget rather than whichever vendor paid for the badge. Statically generated, ISR or fully streamed, whichever fits the traffic. Live preview wired into the editor, cache invalidation that fires when content changes, and Core Web Vitals your marketing team can quote. Moving off WordPress, Drupal, AEM, Sitecore or HubSpot means mapping every legacy URL, preserving SEO equity with 301s, and training editors before the cutover. Our migration playbooks cover 24 platforms in every pairing. Drafting, translation, semantic search and agent-driven publishing, built into the CMS your editors already use, with approval workflows wherever a human should sign off. We run our own content operation this way. Meta fields, structured data and canonical logic live in the content model, so pages can't ship without them. And because search now includes AI answers, every build gets the crawlability work that makes it citable by ChatGPT, Perplexity and AI Overviews. ## Depth in two platforms, not a logo wall We're [one of 15 starred agencies in Sanity's 218-agency partner directory](https://www.sanity.io/agency-partners/roboto-studio), authors of both official Sanity Learn courses on Next.js and creators of [Turbo Start Sanity](https://www.sanity.io/templates/turbo-start-sanity), the starter other Sanity agencies fork. On the Contentful side we're a [Silver Solution Partner](/services/contentful) with Certified Professionals shipping composable commerce builds. We've shipped headless platforms for Tray.ai, Warner Bros, Tabby and Global Cycling Network. Headless builds we've shipped ## Thinking about going headless? The questions we get asked on every headless CMS scoping call. ### What is a headless CMS agency? An agency that designs and builds content platforms where the CMS is decoupled from the frontend. The work spans content modeling, CMS selection and setup, frontend development, migrations from legacy platforms, and the editorial workflow design that decides whether editors love or resent the result. The decoupling is the easy part; the content model is where projects are won or lost. ### Which headless CMS do you recommend? Sanity for most teams: it handles nested structures, reusable blocks and real-time collaboration more gracefully than anything else we've shipped, which is why [it's our default](/services/sanity). Contentful when the requirements lean enterprise: strict governance, heavy localization, procurement that wants a big vendor, which is why [we hold Silver partner status there too](/services/contentful). The recommendation follows your content model rather than our margin. ### Do we need a headless CMS at all? Headless earns its keep when content outgrows one website: multiple channels pulling the same content, structured reuse across many page types, several editors working in parallel, or a frontend your CMS vendor doesn't control. For a small brochure site with one editor, a traditional CMS still does the job, and we'll say so in the scoping call rather than sell you architecture you don't need. ### What frontend do you build with? Next.js, almost always. Server components and streaming fit the headless delivery model, and Vercel's caching primitives make CMS-triggered revalidation reliable instead of hopeful. For content sites with no interactive logic we occasionally recommend Astro. Either way the frontend is built from the content model out, not bolted on after. ### Can you migrate us from our current CMS? Yes. We've moved teams off WordPress, Drupal, Adobe Experience Manager, Sitecore, HubSpot, Webflow and most things in between; the [migration matrix](/migration) documents every pairing. Every migration gets a full URL map with 301s, a schema redesign rather than a lift-and-shift, and editor training before cutover so publishing never pauses. ### How does headless CMS pricing work? Two costs: the platform license and the implementation. Sanity and Contentful both run generous free tiers into paid plans priced on users, locales and API usage. The implementation is usually the larger number in year one, and it's where a clean content model pays for itself, because schema rework after launch costs multiples of getting it right first. We scope both numbers honestly on a call. ### Do you work alongside in-house developers? Constantly. The most common split: your team owns the product and frontend roadmap, we own the content platform, the schema and the migration. When you'd rather run the whole stack yourselves, [our workshops](/services/workshops) transfer it in five days. ### What goes wrong with headless CMS projects? The schema, nearly every time. Builds get modeled around the first page someone wanted to ship, references get stored as strings, and the page builder turns into a maze nobody can edit safely. Most of our rescue work starts there. If you're already living with one of these builds, we'll audit it and tell you honestly whether to fix it, rebuild the schema, or migrate. Most headless projects start on a platform that's run out of road. We've replatformed teams from WordPress, enterprise suites like Adobe Experience Manager and Sitecore, and page builders like Webflow and HubSpot. Each migration page below covers the schema work, the URL and 301 plan, the editor training and the cutover playbook we use. **Tell us what you're building.** A first headless build, an escape from a legacy CMS, or a headless project that went sideways under another agency: we'll scope it honestly in a 20-minute call and tell you which platform fits, including when the answer is neither. From the blog --- ## Next.js app development > Vercel agency partners building serious Next.js apps: dashboards, AI products, content platforms and hybrid SaaS surfaces. **Updated:** 2026-08-25 --- ## Next.js apps, built for the first real version AI chat, customer portal, authenticated workflow, dashboard, CMS-backed product surface. We take early product ideas from brief to production quickly, with the foundations in place for the second and third versions too. We build the interface, app architecture, content and data layer, and Vercel deployment model together. Auth, caching, streaming AI responses, cost controls, observability and previews are part of the product from the start. Companies of all sizes trust Roboto Studio ## Silver-tier Vercel partner A Silver Solution Partner for Vercel, shipping production Next.js across App Router, React Server Components, streaming, ISR, Server Actions and edge routing. Our delivery process is shaped by the ISO 27001 work we have underway, with clear ownership, documented decisions and sensible conventions from day one. Jono and his team are absolute rockstars. They blend technical savvy with practical business sense. They are in lock-step with our website goals and have really made our website come to life. Not just a web dev team, they are trusted advisors and truly aligned with our team. Most of our Next.js work starts when a product idea needs to become real quickly, without building on throwaway foundations. An AI chat interface. A customer portal. A partner dashboard. A CMS-backed product surface with authentication, permissions and billing logic sitting behind the public site. We like that stage. The scope is still moving, but the early technical decisions matter: auth shape, data boundaries, caching, AI provider strategy, deployment model, observability, cost controls and where the app should lean on the server instead of pushing everything into the browser. Our job is to get you to a first version fast, then make sure it is not a dead end. You get something customers can use, investors can understand and your team can keep extending after the first round of feedback. The parts that make apps feel good Dashboards, reports, portals and account views need data fetching that is boring in the best way. We keep the heavy work on the server, stream the slow parts, and avoid client-side soup unless the interaction really needs it. Chat, generation, review queues, agent workflows, embeddings search, streamed responses and human approval steps. We build the interface around latency, state and failure, not just the happy-path demo. A SaaS app still needs landing pages, docs, case studies, changelogs and campaign pages. We wire CMS or MDX content into the same design system so the public site and logged-in product do not feel like two companies. We build the boring gates properly: sessions, roles, protected routes, account states, invitation flows and server-side checks. The app should know who is allowed to do what before the button even renders. Multi-step onboarding, settings screens, uploads, checkout handoffs, CRM syncs and admin actions need validation, optimistic states, retries and clear failure paths. That is where apps feel either polished or held together with tape. Vercel Firewall rules, cache behavior, secrets, preview deployments, analytics events, error reporting, web vitals, redirects and rollback plans. The app is not finished when it compiles. Case studies Next.js is a strong fit when the product has to mix public demand generation with private application logic. The same build can serve SEO pages, onboarding, account areas, customer data, AI chat, file workflows, admin tools and CMS-driven content without splitting the experience across separate frontends. For AI products, we pair the interface work with the orchestration work. [AI Elements](https://elements.ai-sdk.dev/) gives us a strong starting point for chat, tools, sources, reasoning states, attachments and workflow interfaces. [Workflow SDK](https://workflow-sdk.dev/) is where the longer-running parts belong: agent runs, human approval steps, retries, resumable streams, file processing, follow-ups and background jobs that need state and observability. For portals, the shape is different but the standard is the same. Authentication, roles, permissions, dashboards, documents, payments, notifications and support workflows should sit in a clear product architecture. Best practice here means server-first data fetching, small client islands, typed validation, predictable mutations, careful cache boundaries and no hidden mess between the public site and the logged-in app. The Vercel layer matters too. We tune runtime choices, caching, observability, firewall rules, deployment previews and AI Gateway usage so the app is fast, resilient and cost-aware. AI spend and Vercel spend are part of the architecture conversation from day one. Most teams come to us when they need senior Next.js developers who can own the whole app, not a single slice of the frontend. We work as an embedded team: a lead engineer who learns your codebase, designers and developers alongside, and Vercel partner experience to draw on when the platform questions get hard. You can hire us for a fixed-scope first version, an ongoing build, or a focused piece of work like an AI feature, an auth rebuild or a performance pass. Either way you get developers who have shipped production Next.js across App Router, Server Components, streaming and edge routing, and who keep caching, observability, cost controls and the second release in view from day one. ## Thinking about your next Next.js build? The questions that come up on every Next.js scoping call. ### What does a Next.js development agency actually do? Build the application end to end: interface, app architecture, data and content layer, auth and permissions, and the Vercel deployment model. Those decisions interlock, which is the whole argument for one team owning them together. Where it goes wrong is an agency that takes the React layer only and leaves caching, session handling, cost control and observability for someone else to solve later. ### How much does it cost to hire a Next.js agency? We quote after a scoping call rather than publishing a rate card, because the gap between a focused AI feature and a full product build is enormous. What moves the number: how much of the product already exists, whether auth and billing are in scope, how many systems sit behind the interface, and whether you want us shipping after launch. You get a scope and a price for the first version before anyone writes code. ### Can we hire Next.js developers to work inside our team? Yes, and that is how most of our engagements run. A lead engineer learns your codebase and works in your repo and your standups, with designers and other developers alongside as the work demands. You can also hire us for a fixed-scope first version, or for one focused piece of work like an auth rebuild, an AI feature or a performance pass. ### What does your Vercel partnership actually get us? We are a Silver Solution Partner, so Vercel has reviewed our production work and we have a direct line to their team when a platform question gets hard. Day to day it shows up in unglamorous places: runtime choices, cache boundaries, firewall rules, preview deployments, AI Gateway usage and keeping the bill predictable as traffic grows. ### When is Next.js the wrong choice? When the site is pure content with no application logic behind it. A brochure site or a documentation set is often better served by [Astro](/services/astro), and we will tell you that rather than sell you a heavier build. Next.js earns its complexity when marketing pages and a logged-in product have to live in one codebase: SEO pages, onboarding, account areas, dashboards, admin tools and AI features on the same deploy. ### How long until we have something in production? For a first version of a focused app, weeks rather than quarters. Portals and AI products with real auth, billing and third-party integrations take longer, and we scope those in stages. We would rather put a narrow version in front of customers early and extend it than disappear for a quarter and hand over something nobody has used. ### Will you work on an existing Next.js codebase? Often. Inherited apps are a steady part of the work: Pages Router upgrades, App Router migrations, rendering and caching that got away from the team, Vercel bills nobody can explain. We start with an audit and tell you honestly whether the codebase is worth extending or whether the parts causing the pain need rebuilding. From the blog --- ## Sanity support retainer > A named Sanity support retainer from one of 15 starred Sanity partners. Studio upgrades, schema evolution, incident response, performance monitoring and editor support, for builds we shipped and builds we inherited. **Updated:** 2026-08-25 --- ## Support from the people who write the playbook ### A monthly retainer that keeps your Sanity studio upgraded, monitored and moving: schema changes shipped, incidents answered, editors supported. Run by one of 15 starred partners in Sanity's 218-agency directory. Every Sanity studio needs someone on the hook for it. Dependencies age, schemas outgrow their first design, editors hit walls, and the agency that built it has often moved on. The retainer puts our team on that hook: the same people who ship production studios, author the official Sanity Learn courses and maintain Turbo Start Sanity, pointed at keeping yours healthy. ## The unglamorous work that keeps a studio healthy ### Scope agreed in writing at sign-up: upgrades, schema evolution, incident response, monitoring, editor support and a monthly improvements allowance. **Upgrades and maintenance.** Studio versions, plugin updates and dependency patches applied on a schedule, tested before they touch production. **Schema evolution.** Content models drift out of date as the business changes. New page types, field changes and validation rules get shipped through the retainer instead of queueing for a project. **Incident response.** Production issues acknowledged the same business day, with a priority queue ahead of routine work. Specific response windows are agreed per plan. **Performance and SEO monitoring.** Core Web Vitals, structured data and index coverage watched month over month, so regressions get caught by us rather than by your traffic report. **Editor support and training.** A channel for your editors' questions, and training when new team members join or new studio features land. **Monthly improvements allowance.** The small items that never justify a project: a new component here, a workflow tweak there. They get shipped instead of listed. ## We take over Sanity builds other agencies left behind Half the studios we support were built by someone else. The pattern is familiar by now: the build shipped, the invoices stopped, the replies slowed, and the team is left with a studio nobody fully understands. We start those engagements with an audit, tell you honestly what state the build is in, and then either stabilise what's there or fix the schema problems that are worth fixing. [Mario Testino's team came to us exactly this way](/case-study/mario-testino), and the audit-first approach is why that story gets told in our reviews. ## One of 15 starred Sanity partners Sanity's partner directory lists 218 agencies and stars 15 of them. We're one of the 15, which matters for support in a practical way: when an issue is genuinely on the platform side, we have a direct line to Sanity rather than a ticket queue. ## Thinking about a support retainer? The questions we get asked before every retainer scoping call. ### What does the Sanity support retainer include? Studio and dependency upgrades, schema changes as your content needs evolve, incident response for production issues, performance and SEO monitoring, editor support and training, and a monthly allowance for small improvements. The exact scope is agreed at sign-up so there's no ambiguity about what counts. ### Do you only support studios you built? No. Inherited builds are half the point of the retainer. We audit the existing studio first, tell you honestly what state it's in, and either stabilize it as-is or propose the schema fixes worth making. We've taken over Sanity builds from agencies that ghosted, and from agencies that just moved on. ### What are the response times? Production incidents get acknowledged the same business day and jump the queue. Routine requests are scheduled into the monthly allowance. Specific response windows are agreed per plan at sign-up, in writing, because a support promise that lives in a sales call isn't one. ### How does retainer pricing work? A fixed monthly fee scoped to your studio's size and how much change you push through it. We'll give you the number on a scoping call after a look at the build, because quoting blind is how retainers end up mispriced and resented on both sides. ### Do you support Contentful too? Yes. We're a [Contentful Silver Solution Partner](/services/contentful), and the same retainer structure works for Contentful spaces. Mention it on the scoping call and we'll shape the plan accordingly. ### What isn't covered by the retainer? Net-new builds, full redesigns and large [migrations](/services/cms-migration) are project work, scoped separately. The retainer keeps a production studio healthy and moving; it isn't a discounted way to buy a rebuild. When retainer work uncovers something bigger, we'll flag it with options rather than quietly burning the allowance on it. ### Why hire a Sanity specialist for support instead of a general dev shop? Because most Sanity problems are schema and content-model problems wearing a frontend costume. A generalist patches the symptom; we've built enough studios to recognize the cause. Being [one of 15 starred partners in Sanity's directory](https://www.sanity.io/agency-partners/roboto-studio) also means a direct line to Sanity when a platform issue is genuinely on their side. **Tell us about your studio.** Whether we built it, another agency built it, or nobody remembers who built it, the first step is the same 20-minute call: what you're running, what keeps breaking, and what a sensible retainer looks like for it. From the blog --- ## Sanity CMS development agency > Sanity CMS development agency for teams shipping content at scale. Preferred Sanity partner, community ambassadors and authors of Turbo Start Sanity. We build studios, migrate from WordPress and Strapi, and tune Sanity so editors actually want to use it. **Updated:** 2026-08-25 --- Why Roboto Studio? ## Enterprise teams trust us with the build ### Helix in genomics, Global Cycling Network in sports media, Tabby in fintech. All ran on Sanity Studio we built. Each arrived with its own procurement, security review and legacy stack. Our delivery process is shaped by the ISO 27001 work we have underway. ## AI tooling built for your editorial team ### Background agents that research a topic, check every claim against a live source, and hand your editors a sourced first draft. [Semantic search](/blog/building-a-search-bar-with-sanity-embeddings-index-api-and-nextjs) on Sanity's embeddings index. Keyword gaps pulled from Ahrefs. Whatever your content team needs, wired into the studio. Sanity is a database with an editor on top, not a website tool. Your site, your app and whatever you launch next read the same content through one API. Scheduled releases, granular roles and real-time collaboration in one place. Your team runs a launch without a spreadsheet tracking who changed what. Sanity Media Library gives an organization one asset store across every studio, with custom metadata and collections. We add automated OG images and per-channel crops on top. Sanity Connect mirrors your Shopify catalog into the Content Lake so editorial wraps around live product data. [turbo-start-shopify](/tools/turbo-start-shopify) is the starter we ship it on. Presentation gives editors click-to-edit overlays on the real page, next to the studio. They preview a campaign, hit publish and watch the live site change without a redeploy. When the defaults run out of road we extend the studio. Custom input components, internal dashboards and workflow widgets Sanity doesn't ship, all yours to keep. ## 9 in 10 builds we take on are migrations ### Off a legacy CMS, or off a studio another agency walked away from. We have taken over both mid-crisis. The Mario Testino archive came to us at 200% API overage with 160+ redirects missing. Each page below covers the schema, the 301 plan and the cutover. ## One studio, every language ### Tabby publishes across its markets in Arabic and English from a single Sanity studio. Right-to-left styling lives in the component library, not patched on afterwards. One content model, one editorial workflow, every locale. ## What Sanity will cost you The canonical page renders an interactive estimator here. The tables below cover its full answer space, generated from the same logic. Preset usage figures are modelled estimates derived from Sanity's published quotas, except where a preset says measured — those were read off a real account we run, with the client kept anonymous. Roboto Studio does not resell Sanity seats. ### Plan limits | Plan | Editor seats | Documents | Asset storage | Bandwidth/mo | API CDN requests/mo | Uncached API requests/mo | Price | | --- | --- | --- | --- | --- | --- | --- | --- | | Free | Up to 20 | 10,000 | 100 GB | 100 GB | 1M | 250K | $0 forever | | Growth | Up to 50 | 25,000 | 100 GB | 100 GB | 1M | 250K | $15 per seat/month | | Enterprise | Custom | Custom | Custom | Custom | Custom | Custom | Talk to Sanity | Free has no overage billing: exceed a quota and the next step is Growth, not a bill. ### Overage rates | What bills | Included on Growth | Rate over quota | | --- | --- | --- | | Bandwidth | 100 GB a month | $0.30 per GB | | API CDN requests | 1M a month | $1 per 250K requests | | Uncached API requests | 250K a month | $1 per 25K requests | | Asset storage | 100 GB | $0.50 per GB | | Documents | 25,000 | $299/mo for 50,000 documents | | Extra datasets | 2 included | $999 per dataset/mo | An uncached API request costs ten times what the same request costs through the API CDN. That ratio, not content volume, is what turns a badly modelled front end into an overage bill. ### Implementation quality levels | Level | What it means | Request multiplier | Payload multiplier | Share of requests missing the CDN | | --- | --- | --- | --- | --- | | Well modelled | Queries fetch only the fields a page renders, and responses are cached at the edge. | 1x | 1x | 2% | | Typical | Queries pull more than the page needs, and caching is applied unevenly across routes. | 3x | 2.2x | 15% | | Naive | Whole documents fetched from the browser on every render, straight past the CDN. | 12x | 6x | 55% | **Well modelled.** Queries fetch only the fields a page renders, and responses are cached at the edge. - GROQ projections limited to the fields the page renders - One content query per route, resolved on the server - Served through the API CDN and cached at the edge - Uncached requests reserved for previews and webhooks **Typical.** Queries pull more than the page needs, and caching is applied unevenly across routes. - Partial projections, with whole nested objects pulled in wholesale - Several queries per route instead of one composed query - CDN used for most routes, bypassed on the ones that were awkward - Revalidation windows short enough to re-fetch far more than needed **Naive.** Whole documents fetched from the browser on every render, straight past the CDN. - No query projection — whole documents fetched and filtered in JavaScript - Client-side GROQ firing on every render and every navigation - Hitting the uncached API directly, so the CDN never absorbs the load - References resolved one request at a time instead of joined in GROQ ### Estimated monthly cost by archetype and implementation quality | Archetype | Implementation | Pageviews/mo | Documents | Editor seats | Assets | Plan | Estimated monthly cost | Overages | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Simple portfolio | Well modelled | 5,000 | 120 | 1 | 3 GB | Free | $0/mo | None | | Simple portfolio | Typical | 5,000 | 120 | 1 | 3 GB | Free | $0/mo | None | | Simple portfolio | Naive | 5,000 | 120 | 1 | 3 GB | Free | $0/mo | None | | Marketing site | Well modelled | 40K | 400 | 4 | 8 GB | Growth | $60/mo | None | | Marketing site | Typical | 40K | 400 | 4 | 8 GB | Growth | $60/mo | None | | Marketing site | Naive | 40K | 400 | 4 | 8 GB | Growth | $80.69/mo | Bandwidth $18.55; Uncached API requests $2.14 | | High-traffic marketing site | Well modelled | 100K | 2,900 | 22 | 7 GB | Growth | $330/mo | None | | High-traffic marketing site | Typical | 100K | 2,900 | 22 | 7 GB | Growth | $330/mo | None | | High-traffic marketing site | Naive | 100K | 2,900 | 22 | 7 GB | Growth | $441.74/mo | Bandwidth $91.38; Uncached API requests $20.36 | | Documentation site | Well modelled | 180K | 3,500 | 8 | 5 GB | Growth | $120/mo | None | | Documentation site | Typical | 180K | 3,500 | 8 | 5 GB | Growth | $125.72/mo | Bandwidth $5.72 | | Documentation site | Naive | 180K | 3,500 | 8 | 5 GB | Growth | $353.60/mo | Bandwidth $188.48; API CDN requests $0.47; Uncached API requests $44.65 | | High-traffic blog | Well modelled | 750K | 6,000 | 12 | 60 GB | Growth | $233.64/mo | Bandwidth $53.64 | | High-traffic blog | Typical | 750K | 6,000 | 12 | 60 GB | Growth | $309.17/mo | Bandwidth $118.85; API CDN requests $4.80; Uncached API requests $5.52 | | High-traffic blog | Naive | 750K | 6,000 | 12 | 60 GB | Growth | $1,292.68/mo | Bandwidth $880.35; API CDN requests $14.63; Uncached API requests $217.70 | | Multi-brand ecommerce | Well modelled | 2M | 45,000 | 35 | 250 GB | Growth | $2,096.07/mo | Bandwidth $193.05; API CDN requests $5.02; Asset storage $75; Documents $299; Extra datasets $999 | | Multi-brand ecommerce | Typical | 2M | 45,000 | 35 | 250 GB | Growth | $2,315.79/mo | Bandwidth $366.93; API CDN requests $19.46; Uncached API requests $31.40; Asset storage $75; Documents $299; Extra datasets $999 | | Multi-brand ecommerce | Naive | 2M | 45,000 | 35 | 250 GB | Growth | $4,938.48/mo | Bandwidth $2,397.60; API CDN requests $45.68; Uncached API requests $597.20; Asset storage $75; Documents $299; Extra datasets $999 | | News publisher | Well modelled | 3M | 90,000 | 40 | 400 GB | Enterprise | Custom pricing | Bandwidth $304.58; API CDN requests $9.52; Asset storage $150; Documents $299 | | News publisher | Typical | 3M | 90,000 | 40 | 400 GB | Enterprise | Custom pricing | Bandwidth $565.40; API CDN requests $31.19; Uncached API requests $52.10; Asset storage $150; Documents $299 | | News publisher | Naive | 3M | 90,000 | 40 | 400 GB | Enterprise | Custom pricing | Bandwidth $3,611.40; API CDN requests $70.52; Uncached API requests $900.80; Asset storage $150; Documents $299 | **Simple portfolio.** A personal site: a handful of pages, one person publishing. Modelled from Sanity's published quotas, not measured from a client account. **Marketing site.** Twenty or so pages, a small blog, and a handful of people publishing. Modelled from Sanity's published quotas, not measured from a client account. **High-traffic marketing site.** A marketing site at 50k+ sessions a month, with a large publishing team. Seats, documents, assets and traffic measured from a real Growth account in August 2026. On that account bandwidth was the one line that billed, and its measured bandwidth ran well past the modelled figure shown here. **Documentation site.** Deep page count, heavy internal linking, very little imagery. Modelled from Sanity's published quotas, not measured from a client account. **High-traffic blog.** Editorial cadence of several posts a day, with a large image library. Modelled from Sanity's published quotas, not measured from a client account. **Multi-brand ecommerce.** Several storefronts, deep product catalogue, separate datasets per brand. Modelled from Sanity's published quotas, not measured from a client account. **News publisher.** Continuous publishing, a large archive, and a newsroom on staggered shifts. Modelled from Sanity's published quotas, not measured from a client account. Usage figures above are modelled estimates unless a preset's note says measured. Request and bandwidth volume is modelled from the inputs, even on measured presets. Prices and quotas are Sanity's own, checked 2026-08-11. Source: https://www.sanity.io/pricing ## What Sanity costs | Plan | Seats | Documents | CDN API requests/mo | Price | | --- | --- | --- | --- | --- | | Free | Up to 20 | 10,000 | 1M | $0 forever | | Growth | Up to 50 | 25,000 | 1M | $15 per seat/month | | Enterprise | Custom | Custom | Custom | Talk to Sanity | *Sanity's own list prices, verified 2026-07-30. Roboto Studio does not resell Sanity seats.* ## What we've shipped | Figure | What it covers | Resources | | --- | --- | --- | | 500k | Pages migrated to Vercel in six weeks, the largest re-platform we have run | [Tray.ai](/case-study/tray) | | 99.86% | Faster builds, a full workday down to two minutes | [Tray.ai](/case-study/tray) | | 200% | Sanity API overage eliminated on a live photography archive | [Mario Testino](/case-study/mario-testino) | | 160+ | Redirects recovered after another agency's botched migration | [Mario Testino](/case-study/mario-testino) | | 3 months | WordPress and Shopify converged into one Sanity storefront, design through to launch | [Slingshot Bio](/case-study/slingshot-bio) | ## The documentation agent we built ### Ask it anything and it reads your docs and your site's code, then answers from what it actually found. It tracks your codebase instead of a stale snapshot, so marketing stops queuing behind an engineer for answers. ## Sanity's official Next.js courses are both ours [Page building](https://www.sanity.io/learn/course/page-building) and [SEO optimization](https://www.sanity.io/learn/course/seo-optimization). We're the only agency ever to write official Sanity Learn courses. [Turbo Start Sanity](https://www.sanity.io/templates/turbo-start-sanity) is ours too, the starter every new studio here begins from. More Sanity builds we've shipped ## Thinking about building with Sanity? The questions we get asked on every Sanity scoping call. ### What is Sanity CMS? Sanity is a headless, API-first content platform. Editors work in Sanity Studio (a customizable React-based editor that runs in the browser), and content is stored in Content Lake and delivered through GROQ, GraphQL or the Live Content API to whatever frontend you're building. The Studio is fully open source, so the parts that don't quite fit your team can be extended or replaced. That's where most of our work happens. ### How does Sanity CMS pricing work? The Free plan covers 20 seats, 10,000 documents and 1M CDN API requests a month at $0. Growth is $15 per seat per month for up to 50 seats and 25,000 documents, with the same API allowances plus pay-as-you-go overages beyond them. Enterprise is custom pricing for teams that need SSO, custom roles or a dedicated SLA. The honest answer most agencies skip: budget for the implementation and content-modeling work too. The license is usually a small fraction of total cost in year one, and a well-modeled studio earns that fee back inside the first few campaigns. ### Is Sanity free? Yes, and the free plan stays free with no time limit. It covers 20 user seats, 10,000 documents, 1M CDN API requests a month, 250K regular API requests and 100GB of assets. You outgrow it when you need more than 20 seats, more than 10,000 documents, scheduled publishing, private datasets, or roles beyond admin and viewer. The next step up is Growth at $15 per seat per month. ### How much does Sanity cost per user? $15 per seat per month on the Growth plan, charged for editor seats only, with viewer seats free. Growth runs up to 50 seats, so a team of 10 editors pays $150 a month before any overages. Under 20 seats the Free plan costs nothing. Past 50 seats, or if you need SSO or custom roles, you move to Enterprise and Sanity quotes you directly. ### How long does a WordPress to Sanity migration take? For a site of 500 to 2,000 pages, 4 to 8 weeks end to end. Mapping your existing content model to a Sanity schema is 1 to 2 weeks of that. Editor training is a 2 hour onboarding session and most teams publish confidently inside a week. Livemore's marketing team shipped 12 campaign pages in their first month on the studio we built. ### Sanity CMS vs WordPress: which one should we move to? If your team is mostly publishing blog posts on a single website and you're happy with WYSIWYG editing, WordPress is fine. If you're publishing structured content into multiple channels (a website, a mobile app, an in-store screen), or your editors keep hitting WordPress's limits with custom fields and ACF workarounds, Sanity is the right move. We run WordPress-to-Sanity migrations regularly: we map every legacy URL, preserve SEO equity with 301s, redesign the content model around how editors actually work, and train the team before cutover so they're ready to publish on launch day. ### Sanity CMS vs Strapi: what's the difference? Strapi is self-hosted and open source; Sanity is hosted with a much more polished editor and a real-time Content Lake. Strapi makes sense if data residency or self-hosting is non-negotiable and you have the DevOps budget to run it. Sanity makes sense for almost everyone else. The editor, the live preview, the Presentation tool and scheduled publishing are years ahead, and you don't pay for the infrastructure to keep them running. We've migrated teams in both directions and Sanity wins on editorial velocity in most scenarios. ### Can Sanity be self-hosted? Sanity Studio (the editor) is open source and runs anywhere you deploy a Next.js or Vite app: Vercel, your own infrastructure, behind a VPN. The Content Lake (the database) is Sanity-hosted and not available as a self-hosted product. For teams with strict data residency requirements, Sanity offers regional hosting and Enterprise data-handling agreements; we'll walk you through the options on a scoping call. ### What framework do you recommend with Sanity? Next.js, almost always. The App Router, server components and streaming model fit Sanity's GROQ-on-the-server pattern perfectly, and Vercel's caching primitives make `revalidateTag` invalidations from Sanity webhooks trivial. For brochure sites with no interactive logic we'll sometimes recommend Astro instead, but nine out of ten production builds end up on Next.js, which is why [Turbo Start Sanity](https://github.com/robotostudio/turbo-start-sanity) is a Next.js + Sanity starter. ### My last Sanity build was a mess. Is Sanity actually the problem? Almost never. Every time we've inherited a "painful" Sanity studio, the schema turned out to be the problem: references modeled as strings, page builders without thumbnails, validation rules nobody ever wrote. A good Sanity studio is built around how editors actually work, and most agencies skip that step. If you've got a Sanity build that's making editors miserable, we'll happily audit it and tell you honestly whether to fix it, rebuild the schema, or migrate to something else. From the blog --- ## Headless Shopify agency > We build custom headless Shopify storefronts on Next.js. Configurators, custom buying journeys, sub-second product pages, and the conversion that comes with them. **Updated:** 2026-08-25 --- ## Commerce experiences worth building headless ### Configurators, build-your-own-bag flows, custom buying journeys, and editorial pages your marketers control. The things that make a brand feel premium and a store convert. We build the storefront on Next.js and let Shopify do what it's great at: products, inventory, cart, and checkout. You get a site that loads fast, ranks, and can do whatever the brief asks for. Companies of all sizes trust Roboto Studio What you can build headless Configurators, build-your-own-bag flows, made-to-order steps, lookbooks, and multi-step buying journeys. The kind of thing premium brands ship to make buying feel like part of the product. On a custom Next.js frontend that's just another component to build. Campaign pages, editorial, structured product narratives, and navigation all live in a CMS your marketers edit directly. They ship pages without a developer ticket, and Shopify keeps handling products and checkout. Server rendering, image optimisation, and a Vercel edge network in front of every page. Sub-second product pages and listings at scale, including catalogues in the thousands. The result is Core Web Vitals you actually pass. A theme hands every product the same buy button. Most premium catalogues need more than that: the same fireplace surround can be a made-to-order commission for one customer and an in-stock purchase for the next. On a headless storefront the click decides the journey. An enquiry lands in your CRM (Salesforce or whatever you run) with the configured spec attached, ready for your sales team to quote, and a purchase goes straight through Shopify checkout. The journeys themselves are where headless earns its keep. A product configurator that reprices as the customer picks materials and sizes. A bundle builder that nudges order value up as the box fills. Deposits and lead times for made-to-order pieces. On a theme, each of those is an app subscription and a compromise. On Next.js they're components we design around your products, with Shopify still running cart, payment, and fulfilment underneath. Moving off Magento, WooCommerce, or an ageing theme store takes more than a storefront redesign. We run the whole migration as a service: catalogue, customers, orders, and URLs all land on a new Shopify instance, with a purpose-built headless storefront on Next.js in front of it. The step most teams underestimate is the 301 redirect map. We build it before launch so every old URL re-points to its new home, and the rankings you've spent years earning hold through the cutover. Teams arrive in one of two lanes, and both feed the same roadmap. Some want a new build: a brand-led storefront from the ground up on Turbo Start Shopify, with the custom functionality your roadmap needs and a content setup your marketing team owns. Others are already on Shopify and need a storefront replatform: we rebuild the frontend on Next.js for Core Web Vitals and conversion while the commerce backend stays exactly where it is. The only real difference between the lanes is whether your commerce data moves with you. If you're comparing Shopify migration agencies, ask each one to walk you through their redirect plan before anything else. Ours ships on launch day, and we watch Search Console through the cutover until the numbers settle. And if it's your content platform that needs to move rather than your store, that's our [CMS migration service](/services/cms-migration). Most Shopify speed optimisation stops at compressing images and pruning apps, and the gains erode as soon as the next script lands. The theme itself sets the ceiling: app scripts, render-blocking assets, and a checkout you can't fully tune. A headless frontend on Next.js removes that ceiling. We server-render the pages that matter, defer the rest, and serve everything from the edge. Faster pages convert better and rank better. Before we start, we benchmark your Core Web Vitals (LCP, INP, and CLS) alongside the key buying journeys, then measure the same numbers after launch, so the speed and conversion gains are something you can see rather than take on faith. If your current Shopify storefront feels slow, we'll tell you exactly what's dragging it down before you commit to anything. My best experience with a consulting company. The results were delivered faster than expected and with top quality. Jono ensured I understood the process and suggested a great approach. Both execution and communication were flawless. Shopify builds we've shipped Every build starts from Turbo Start Shopify, the open-source headless commerce starter we maintain. Next.js 16 for the frontend, the Shopify Storefront API for products, collections, cart, and checkout, and Sanity as the editorial layer for pages, blog, navigation, and SEO. Vercel handles hosting, edge caching, image optimisation, and observability. None of it is exotic. It's the same stack we run in production for clients, type-safe from the database to the page, and you can read every line of it [on GitHub](https://github.com/robotostudio/turbo-start-shopify?utm_source=roboto_studio_website) before you hire us. ## Thinking about headless Shopify? The ones we get asked the most ### What is headless Shopify? Shopify still runs the store: products, inventory, cart, checkout, customers, and fulfillment. Next.js renders the storefront, and the two talk over the Shopify Storefront API. Customers see a custom site backed by Shopify, and your team edits content in a CMS instead of theme code. Our [guide to headless Shopify](/blog/shopify-headless-guide) walks through the costs, the tradeoffs, and when a vanilla theme is the smarter call. ### Can you build custom functionality like a build-your-own-bag configurator? Yes, and it's the main reason to go headless. Configurators, made-to-order flows, product personalization, lookbooks, and multi-step buying journeys are all just components in a Next.js app. If a premium brand has a storefront experience that feels genuinely bespoke, it's almost certainly headless. ### We're already on Shopify. Is replatforming the storefront worth it? A replatform earns its keep when page speed is hurting conversion, when the marketing team is stuck waiting on developers, or when the design you want won't fit the theme. If none of those apply yet, we'll say so. We benchmark your current Core Web Vitals first and give you an honest read before you commit to anything. ### Does this work on standard Shopify, or only Shopify Plus? Both. Headless works on standard Shopify. Plus adds checkout extensibility, multi-store, scripts, and higher API limits, which matter once volume or compliance demand them. Slingshot Bio runs on Plus, Jamb doesn't, and the build pattern is the same. ### What happens to SEO during a replatform? It's planned from day one. We map every URL before launch, ship redirects on launch day, wire internal links through CMS references rather than hardcoded slugs, and watch Search Console and Vercel through the cutover. Jamb's rebuild merged two legacy URL spaces and held its rankings. The [CMS migration service](/services/cms-migration) covers the mechanics in depth. ### What does a full Shopify migration service include? Everything that makes the store yours: the product catalog, customer accounts, order history, and every URL. We move the data onto a new Shopify instance, build a purpose-built headless storefront on Next.js, ship a 301 redirect map so old URLs point at their new homes, and run the cutover with Search Console open. Your customers keep their accounts and your rankings hold. ### Can we keep our Shopify apps? Backend apps that touch inventory, orders, customers, fulfillment, or accounting stay. Frontend apps that inject scripts into a theme usually move into the Next.js codebase instead, which is faster and cheaper than paying for them every month. **Tell us what you're building.** A new headless storefront, the custom functionality your roadmap needs, or a Shopify store that's gone slow. We'll scope it honestly in a 20-minute call. If you'd rather look under the hood first, [Turbo Start Shopify](https://github.com/robotostudio/turbo-start-shopify?utm_source=roboto_studio_website) is the same starter we ship on production builds. --- ## Workshops > Hands-on workshops with two senior Roboto developers. Skill your team up on Sanity, Next.js, and Vercel Workflow SDK agents, with real output shipped by the end of the week. **Updated:** 2026-08-25 --- ## Two senior Roboto developers, sat alongside your team for the week. You learn how we build, by building with us. ### Small groups, fast pace, AI-augmented from the start. The work that used to take six weeks of trial and error compresses into days, because we have already run into every wall on the way. Bring your laptops and the editor you like. Cursor, Claude Code, Codex, or anything else with modern AI assist works. We share a short setup checklist once a date is on the calendar so day one starts at full speed. Our workshops are usually pitched at engineering teams of two or more, with content or marketing stakeholders welcome where the workshop format suits them. ## Get your team confidently building with Sanity and Next.js in a week flat. ### Two of our developers work alongside your team for five days. We use [Turbo Start Sanity](https://github.com/robotostudio/turbo-start-sanity) as the launchpad, and lean on AI to keep the pace up. If you have a team of two or more developers ready to put the work in, this gives you the fastest possible path to a production-ready foundation. Knowledge stays in-house. ### What you walk away with - A deployed Sanity studio and a deployed Next.js front end. - Content models built around your real domain, not a generic starter. - Working preview, drafts, and a deployment pipeline you understand. - Your team trained to extend the schema, build new sections, and onboard the rest of the org. [Book this workshop](/contact) ## Ship a durable agent that actually does something for your business. Then know how to ship the next one. ### Vercel Workflow SDK is the cleanest path to production-grade agentic workflows we have used. This workshop walks your team through it on a real task in your stack. Scope is shaped to your team. Some clients want a one-day intensive that ends with a deployed agent. Others want a longer engagement that ships two or three workflows and trains the team to own them. Talk to us and we will scope it together. ### What you walk away with - A durable agent running in your environment, doing real work. - Integration with the tools and APIs your team already uses. - A clear mental model of when an agent is the right answer, and when it is not. - Your team confident enough to design and ship the next workflow without us. [Talk to us about this workshop](/contact) Companies of all sizes trust Roboto Studio ## Agency partners, community ambassadors, and the team behind Turbo Start Sanity ### We've built with Sanity and Next.js for years, and we put real work into teaching structured content and sharpening how teams use Sanity Studio. We have published a lot of educational content, open-sourced [Turbo Start Sanity](https://github.com/robotostudio/turbo-start-sanity) as our opinionated starter, and we now ship [agentic workflows](/services/agentic-workflows) on Vercel for our own pipeline. The workshops are the version of that knowledge your team can take home. Jono and his team are absolute rockstars. They blend technical savvy with practical business sense. They are in lock-step with our website goals and have really made our website come to life. Not just a web dev team, they are trusted advisors and truly aligned with our team. Client work we've shipped ## Need a workshop on something else? We design bespoke workshops around the topics our clients actually want to learn. Headless Shopify, content modelling, AI-augmented dev flow, custom integrations. Tell us what your team needs to skill up on and we will tell you whether we are the right fit. ## Thinking about a workshop? Here are the questions we hear most often ### How big should our team be? Two to four people works best. For the Sanity and Next.js workshop we ask for at least two developers on your side. For the agent workshop, one engineer plus a product or content stakeholder is usually the right mix. Larger groups split the attention of the room and slow the pace. ### What do we need to bring? Laptops with your editor of choice, AI assist enabled, repo access, and any existing designs or content models. We share a setup checklist once a date is on the calendar so day one starts at full speed. ### Do you run workshops remote or onsite? Both. Remote is the default, runs over Zoom plus shared repos and Slack, and works for teams across timezones. Onsite is available for teams in or near our London base, and for teams happy to fly us in. ### Do we need to use a specific editor or AI tool? No. Any modern editor with AI assist is fine. Cursor, Claude Code, Codex, GitHub Copilot, JetBrains AI all work. We bring our own setup so your team can compare approaches as the week goes. ### What if the workshop we want isn't listed? Talk to us. We design bespoke workshops around the topics our clients ask for. If you have a specific stack, integration, or workflow you want to skill up on, scope it with us and we will tell you whether it is a fit. ### How quickly can you run one for us? Typical lead time is two to four weeks from first contact to day one. We hold a small number of workshop slots open each quarter, so urgent requests can sometimes land sooner if a slot opens up. ## Ready to book a workshop? Pick the workshop that fits, or tell us what your team wants to learn. We will follow up with available dates and a short scoping call. --- # Case Studies ## Slingshot Bio > Roboto converged Slingshot Bio's WordPress and Shopify sites into one headless Shopify build on Next.js and Sanity, instrumented end to end and AI-ready. **Published:** 2026-05-27 | **Updated:** 2026-07-23 | **Client:** Slingshot Bio | **Technologies:** Shopify, Sanity, Next.js, Vercel | **Services:** Web development, Migration, Brand --- Slingshot Bio makes synthetic cell mimics: engineered particles that stand in for real T, B, and NK cells on any flow cytometer and hold steady where donor samples drift batch to batch. By the time they reached us they had outgrown their setup, a WordPress marketing site on one domain and a separate Shopify store on another, with no shared design, content model, or data between them. We were brought in to converge the two into one headless platform: Next.js and Sanity on Vercel, Shopify kept purely for checkout, built on [turbo-start-shopify](https://github.com/robotostudio/turbo-start-shopify), our open-source Shopify and Sanity starter. Design through to launch in three months. The storefront had to make a dense, technical catalogue easy to move through, so the science reads clearly and a researcher gets from landing page to cart without friction. We instrumented the entire path in PostHog and stitched identity across the headless boundary, so we can follow one session from first landing through to a completed Shopify checkout, even though checkout runs on a separate domain. Session replay sits on top, so the team can watch real researchers move through the site and see where a high-value order stalls or closes. A scientific catalogue is unusually structured. Every control carries markers, reactivity, pack sizes, application notes, and a certificate of analysis, and a researcher needs to scan any product and know exactly what they are holding. We modelled all of it in Sanity, so each product is structured data rather than a hand-built page. Spec sheets, panels, and reactivity render from one source, and the team can publish a new product or COA without a developer. New entries slot into the system as the catalogue grows. Underneath, it is a [headless Shopify build](/services/shopify). Sanity owns content, Shopify is reduced to checkout, and Next.js on Vercel keeps the full catalogue filtering instantly, even as families and markers are added. We also built for how labs actually buy. Most controls sell through a mix of cart and enquiry, and a purchase usually needs sign-off, so a researcher can turn a basket into an itemised, branded PDF and send it to a principal investigator or procurement to approve. The cart holds for weeks, so they can return and check out once the approval lands. The cutover was clean. We inventoried every page and resource across both old sites, mapped each to its new home, and built a custom Shopify theme whose only job is redirects, so nothing broke and the search equity carried across. The platform launched AI-ready: JSON-LD on every product, collection, and resource, content negotiation that serves clean Markdown to AI crawlers alongside the HTML, an llms.txt, a Markdown sitemap, and a sitemap whose timestamps track Sanity directly. Marketing publishes in Sanity, and the structured data, the sitemap, and the crawlable Markdown all stay current on their own. The native Shopify basket waits on the server for every change. We replaced it with a headless commerce cart on Next.js that updates optimistically: add a control or change a quantity and the line items and total move at once, the price animating to its new value while Shopify's Storefront and Cart APIs confirm in the background. Nothing blocks on the round trip, so the basket reads as markedly faster than native Shopify. For a researcher assembling a multi-line order across panels and pack sizes, it keeps pace with how fast they work. --- ## Jamb > We rebuilt Jamb on Sanity and Next.js, merging two legacy PHP sites into one calm catalog without losing the SEO equity their antique and reproduction collections had built up. **Published:** 2026-05-25 | **Updated:** 2026-07-23 | **Client:** Jamb | **Technologies:** Next.js, Sanity, Shopify, Vercel | **Services:** Brand, Web development, Migration --- Jamb and Hawker Antiques had just merged when we picked up the project. Two PHP sites, 70+ duplicate meta descriptions, and a custom CMS where adding a furniture category meant hoping nothing else fell over. The catalogue runs from 18th-century Irish chimneypieces to six-figure antiques, the kind of stock collectors and interior designers wait years for. Most of it sells through enquiry rather than a cart. We worked with Toni Howarth and the [benweaver.eu](http://benweaver.eu) team to merge the two sites into one and rebuild the platform on Sanity, Next.js, and Shopify. Jamb's catalogue runs on two flows. Some pieces sell through checkout, most still sell through enquiry. [Headless Shopify](/services/shopify) lets the same product record route to either, with variations (sizing, finish, edition) carrying through when there's a cart to render. The rest stays on the enquiry path the team has spent years tuning. For the homeware that carries stock, Shopify's Buy Button drops in beside the Sanity catalogue with no separate storefront and no second CMS. Adding or pulling a category takes minutes, and the rest of the site never has to know about it. Ben Weaver led the creative direction. Our job was to tighten the lens in Figma so the designs survived the trip into code. We worked through every breakpoint from mobile, where Jamb's best photography has to carry the whole page, up to 5K iMacs where the marble grain in a chimneypiece needs to stay sharp at 1:1. The handoff to development was a spec. Every breakpoint and interaction was defined, not implied. The site's design intent doesn't drift between Figma and production. Most rebuilds break somewhere in the URL space. Jamb had two URL spaces merging into one, decades of organic equity to keep, and antique-specific terms that rank slowly and stay ranked for years. Before launch we built a full sitemap spanning both legacy sites, mapped every page into the new one, and wired internal links through Sanity document references rather than hardcoded URLs, so a slug change in one place doesn't cascade into 404s anywhere else. After launch we watched Search Console and Vercel observability for even the smallest 4xx and 5xx errors, and shipped redirects the same day. Rankings held through the cutover. jamb.co.uk launched with no drama. Hundreds of enquiries came through in the first week, and page loads stayed sub-second. Build times went from 30 minutes to three across 2,000+ pages, so the team can push changes without waiting on a developer. A wider [Shopify Connect](https://www.sanity.io/docs/apis-and-sdks/sanity-connect-for-shopify) roll-out is queued for the parts of the catalogue that suit a checkout flow, but there's no rewrite on the horizon. --- ## Airtime Rewards > We built cashback platform Airtime Rewards a Gatsby site on Netlify: 0.4s First Contentful Paint, 0.6s to interactive, and perfect Lighthouse scores. **Published:** 2025-03-14 | **Updated:** 2026-07-23 | **Client:** Airtime Rewards | **Technologies:** Gatsby, Prismic, Netlify | **Services:** Web Development, UI Design --- Our collaboration focused on delivering a high-performance Gatsby.js website, with Prismic as the [headless CMS](/services/headless-cms), optimized for exceptional speed and user experience. By implementing advanced lazy loading techniques for images and using Netlify's caching and asset optimization, we achieved remarkable load times: just 0.4 seconds for First Contentful Paint and 0.6 seconds to interactive. Our meticulous approach to SEO included building a comprehensive sitemap that clearly defined page relationships, ensuring optimal indexing by Google. This attention to detail resulted in perfect scores across Google's Lighthouse audits for performance, accessibility, best practices, and SEO, significantly enhancing Airtime Rewards' online visibility. Thanks to these enhancements, Airtime Rewards successfully surpassed one million users and secured prominent placement on EE's customer homepage. While their exceptional leadership and strategy drive their impressive growth, we're proud to have contributed by creating a website that is visually appealing, exceptionally fast, and optimized for continued growth. --- ## Livemore Mortgages > We built Livemore Mortgages a modular page builder with Sanity and Next.js. Landing pages that took weeks now ship in hours, and updates go live 5x faster. **Published:** 2025-03-14 | **Updated:** 2026-07-23 --- Their rigid template system couldn't adapt to market shifts, putting them at a competitive disadvantage against more agile firms who could rapidly deploy targeted content in response to emerging opportunities. We built a modular page builder with [Sanity](/services/sanity) and [Next.js](/services/nextjs), featuring drag-and-drop content blocks with intuitive editing interfaces. The implementation included Sanity Image optimization with hotspot controls and granular SEO settings for each component. The results were immediate: landing page creation time dropped from weeks to hours, updates went live 5x faster, and their marketing team independently launched 12 new campaign pages in the first month, eliminating their technical bottleneck and accelerating their market responsiveness. --- ## Mojo Mortgages > Mojo Mortgages needed a redesign shipped in four weeks. We rebuilt their frontend and four mortgage landing pages on Gatsby across two headless CMSs. **Published:** 2025-03-14 | **Updated:** 2026-07-23 --- We cleaned up their codebase, ensuring their blog and homepages all worked together flawlessly despite using [two separate headless systems](/services/headless-cms). Their mortgage comparison tool really needed specialized landing pages that could reduce friction and boost conversions across different mortgage categories. We architected four rock-solid static landing pages targeting key mortgage verticals on their Gatsby foundation. Our technical approach centered on performance-first development, using SVG-centric design and a lean component architecture that cut unnecessary bloat. We built a lean user experience that cut excessive form fields while still capturing comprehensive data. The solution launched on schedule for their acquisition announcement, with performance metrics that spoke volumes. Their Product Owner didn't mince words, calling the pages "lightning fast", high praise that led to an immediate project extension for four additional subpages built on the same battle-tested architecture. --- ## Session > Working with Motto, we built an editorial platform for Session on Next.js and Sanity: a flexible page builder, live preview, and pixel-perfect images. **Published:** 2025-03-14 | **Updated:** 2026-07-23 --- Our solution used [Next.js's built-in image optimization capabilities](/services/nextjs) to ensure pixel-perfect clarity across all devices, handling edge cases like right-sizing, device-to-pixel ratio, and lazy loading. The implementation featured a flexible page builder methodology that allowed the Session team to manage content without rigid restrictions. We created customizable components that could handle complex design requirements while remaining adaptable to changing needs. The platform integrated Vercel's CDN and Edge Functions for lightning-fast content delivery, while Sanity Studio's Content Previews enabled confident experimentation and deployment. The result was a lean content management system that kept image quality high while delivering optimal performance and SEO benefits. --- ## Tray.ai > Migrating hundreds of thousands of pages, re-platforming and extending for the leading composable AI integration platform **Published:** 2025-01-15 | **Updated:** 2026-07-23 | **Client:** Tray.ai | **Technologies:** Contentful, Next.js, Vercel | **Services:** Development, Migration, Consultancy --- We slashed Tray.ai's build times by **99.86%**, shrinking a full workday to just **two minutes**. In just six weeks, we migrated **half a million pages** onto Vercel through a progressive strategy and rapid iteration. Now, a lightning-fast browsing experience for more than a million monthly visitors. ## Legacy Next.js 9, fragile AWS Serverless functions, and 250 MB packages choked every deploy. Previewing changes meant full prod builds; form logic lived in a separate repo; failed deploys were weekly rituals. Marketing releases stagnated, engineers dreaded merge-days, and SEO rankings slid as pages crawled to load. ## We consolidated five repos into a single Turborepo monorepo on Next.js 13, introduced hybrid SSG + SSR routing for 25 complex page types. We re-platformed forms to Vercel Functions with reCAPTCHA, migrated from styled-components to Tailwind CSS through a progressive strategy, and added `next/link` pre-fetching and `next/image` optimisation. The move trimmed bundle size, simplified developer workflows, and hardened security behind Vercel's firewall. ## [Contentful](/services/contentful) underwent a full declutter. We scrapped bloated models, built a fresh page-builder with live preview. We gated resources behind smart forms. Marketing can now ship pages in minutes, without pinging engineering. Real-time preview across any route lets writers catch copy issues before they publish. ## Deployments that once took 24 hours now finish in 120 seconds. Page-load times dropped by an order of magnitude; Vercel Speed Insights scores sit in the high 90s. During two DDoS waves, the new static-first strategy served traffic with **0% downtime**. > Roboto Studio migrated our project to Vercel and reduced build times from a full day to just 2 minutes for faster rollouts and more agile updates. > — Co-founder & CEO, Tray.ai --- ## Synacti > We built Synacti's site with v0 generating over 90% of the code, then added a Matter.js interactive hero, a theme picker, and Sanity live content editing. **Published:** 2024-12-10 | **Updated:** 2026-07-23 --- We built over 90% of the site with v0.dev, which let us move fast without dropping quality. That freed up time to hand-build the harder features: a Matter.js interactive element and a user-controlled theme picker. The site runs on the Next.js App Router (`/app` directory), which keeps Core Web Vitals strong, and it's deployed on Vercel for fast global delivery. We wired up [Sanity CMS](/services/sanity) with its Live Content API (Presentation) so Synacti get a visual page builder with real-time updates across the site. The result is a fast, engaging site with a content management setup their team actually enjoys running. --- ## Mario Testino > From Sanity overages to instantaneous publishing, we brought Mario Testino into the fast lane, and did it in style. **Published:** 2024-11-20 | **Updated:** 2026-09-04 | **Client:** Mario Testino | **Technologies:** Sanity, Next.js, Vercel | **Services:** Development, Consultancy --- Who doesn't know Mario Testino OBE, in the media industry, one of the legends who has photographed everyone from Kate Moss to Princess Diana and even shot for Gucci, Burberry, Versace, Chanel, and Dolce & Gabbana. He has worked for Vogue, Vanity Fair, and museums across the globe, including the National Portrait Gallery in London and the Museum of Fine Arts in Boston. And a photographer of his level needs a website that is not just artistically stunning but also flawlessly editable. When we first inherited the Mario Testino project, they were in a state of crisis. Broken Vercel pipelines, 200% overages on Sanity, and a botched migration with 160+ missing redirects. We jumped straight in, without a second thought. We helped them to set up a company repo, reconnect severed connections, and started tackling the Sanity overage issue. ## Testino's site was already built on Sanity, which might make you think things would be easier. But here is the spoiler: they weren't. The whole project was sitting on a pile of deprecated dependencies, the kind that break if you so much as look at them. Imports were busted, libraries were abandoned, and updates were basically a threat. So we ripped everything back to [vanilla Sanity](/services/sanity). Stripped the excess code, removed unnecessary libraries, rebuilt the editorial flow, and dropped in Live Content API so edits didn't feel like "press publish and wait five minutes for the universe to respond." While refactoring, we fixed every redirect, cleaned up navigation so crawlers could actually read it, and made sure creators and search engines got the same smooth experience. Then we rewrote every page, image implementation, and data query from scratch. ## Geoff and the Testino team saw the Turbo Start Sanity template experience and wanted exactly that. So we gave them exactly that. Now they can add exhibitions anywhere, spin up blogs and press releases, and reorder content without waking up a developer at 11 pm. We also experimented with Geoff on layouts and added the scoop section. It is a tiny spotlight for "never-before-shared" Testino stories. And because this is photography, we dialled in image hotspotting so every shot looks flawless on anything from an iPhone to a retina monitor that costs more than our rent. SEO needed a little TLC after the previous migration missed all the redirects. With Francesco, their SEO expert, we worked hand-in-hand to rebuild structured data with JSON-LD, clean up crawl paths, and get Google to stop ghosting the site. We also slapped on Vercel Bot ID because scrapers treat photography sites like an all-you-can-eat buffet. With the foundation finally stable, we shipped two new sections, Selected Press and MT World, and the site now behaves the way Testino's work deserves. ## The Testino team was genuinely thrilled, so much so that Geoff Cooper's words about our work now sit proudly on our homepage. The site went from broken and unpredictable to fast, stable, and fully editor-friendly. Francesco, their SEO expert, tracked improvements daily as we restored redirects and implemented proper JSON-LD. Organic visibility climbed back, schema is now set-and-forget, and we don't want to jinx it… But the whole site crawls flawlessly. Operationally, the upgrade was immediate. The team can add exhibitions, publish press releases and blogs, reorder content however they want, and rely on hotspotting to keep every image perfectly framed. No broken layouts, no surprise dev emergencies. We're now adding AI-powered upgrades like automatic alt-text, richer SEO content, and future e-commerce functionality. --- ## Hungry Pumpkin > E-commerce optimization for Hungry Pumpkin: one-click checkouts, inventory management, and WordPress training so their team can manage products themselves. **Published:** 2024-10-28 | **Updated:** 2026-07-23 --- Our hosting recommendations delivered a rock-solid foundation with near-perfect performance scores, complemented by a set-and-forget maintenance plan that keeps their award-winning site running smoothly. The results spoke for themselves - shortly after launch, Hungry Pumpkin earned a Travellers Choice award, thanks to their delicious offerings and our technical partnership. From latte art to layout changes, we [trained their team](/services/workshops) and equipped them with tools to focus on what matters most: serving up incredible full-English breakfasts and authentic Italian dishes. --- ## St Mary's Church > We rebuilt the website for St Mary's Church, a historic Nottingham landmark, on WordPress with a simplified admin so parish staff can update it themselves. **Published:** 2024-10-15 | **Updated:** 2026-07-23 --- Its existing website suffered from outdated design and rigid content management, making it difficult for staff to keep their schedule current, a critical function for announcing services, community events, and parish news to locals. The technical challenge involved creating a solution that was visually befitting of the landmark's heritage while being intuitive enough for the church's non-technical staff to manage independently. We collaborated closely with the Vicar to develop a WordPress solution with an off-the-shelf theme and tailored administrative interface. By building a simpler calendar management system with recurring event support, we eliminated the previous workflow bottlenecks that had plagued their communication efforts. We're happy to say that they're still using the same infrastructure from 2018. *We build websites to last.* --- ## 1st Call Immigration > 1st Call Immigration’s WordPress site cost four figures a year in hosting. We rebuilt it on Sanity and Next.js with near-perfect Lighthouse scores. **Published:** 2024-09-30 | **Updated:** 2026-07-23 --- Because of the number of plugins and the transition to the Gutenberg editor, it had prevented content updates for five years due to repeated PHP errors. As a small immigration advisory business in Nottingham, they needed a cost-effective solution that would maintain their local SEO presence while providing an intuitive content management system that wouldn't require technical expertise to update regularly. We implemented a barebones [Sanity](/services/sanity) and Next.js solution that drastically reduced hosting costs while delivering near-perfect lighthouse scores across performance, accessibility, and SEO metrics. By tailoring the content blocks specifically for immigration services content and using Sanity's real-time preview capability, we transformed the client's editorial experience. --- ## Tabby.ai > Helping the UAE's most prolific Pay in 4 merchants scale their design system and composable infrastructure. **Published:** 2024-09-10 | **Updated:** 2026-07-23 | **Client:** Tabby.ai | **Technologies:** Sanity, Next.js, Vercel | **Services:** Development, Migration, Consultancy, Design --- Tabby AI is a leader in the UAE's "Pay in 4" space, and their rapid expansion had outgrown their setup. They needed to scale up their design system and composable infrastructure to work hand-in-hand with their website. Our job was to build the component library that sits under Tabby AI's whole digital experience. That meant more than code: an accessible interface that worked across their markets, with internationalisation (i18n) and accessibility built in from day one. ## The whole process felt like a true partnership between our crew and the brilliant developers at Tabby AI. We rolled up our sleeves together, focusing on creating a component library that wasn't just visually slick but also incredibly functional and easy for their team to use and extend. The latest Storybook component browser let us document and test each component in isolation, which made the whole build faster. Both teams could see progress clearly and iterate quickly. A special shout-out to their designer, Sergii, who documented and scoped components in a Figma environment like I've never seen before. We weren't just delivering code, we were building a shared understanding and a toolset their internal teams could keep extending long after our involvement wrapped up. ## Alongside scaling the frontend infrastructure, we tackled the backend migration: upgrading Tabby AI's content platform from Sanity v2 to v3. This wasn't just a version bump but a golden opportunity to slash accumulated development debt and completely rethink their content structures. We checked their existing schemas, identifying pain points and areas for simplification. The move to Sanity v3 allowed us to implement much more reusable data structures, meaning content could be created once and surfaced dynamically across different parts of their site and apps. This made the editors' lives easier and drastically reduced redundancy and potential inconsistencies. The result was a faster, cleaner, and far more [maintainable Sanity Studio](/services/sanity-support) that gave them a gold standard in editorial experiences. --- ## Simply Thrilled > A video portfolio site for Simply Thrilled on Webflow, delivered two days before a three week deadline while training their team to run it themselves. **Published:** 2024-08-22 | **Updated:** 2026-07-23 --- Their existing WordPress platform couldn't do their work justice due to poor performance and limited customization. The challenge: deliver a visually stunning site that the client could independently maintain within tight constraints. We built a performance-optimized Webflow MVP with compressed media assets and lazy-loaded video content. Through collaborative on-site sessions, we simultaneously developed the site while [training their team](/services/workshops). The results: launch two days early and notable client acquisitions, including an [NHS project featuring Nottingham's own Vicky McClure](https://www.simply-thrilled.com/). As the client noted: > "A special shoutout for the passion they put in to optimising the website and ensuring the site as fast as possible through Webflow." The new professional presence enabled Simply Thrilled to secure higher-profile work while maintaining complete content autonomy through the lean design system we established. --- ## Savory & Partners > Discover how Roboto Studio transformed Savory & Partners' website performance, boosting Lighthouse scores from 60 to 90+, while rebuilding their development process around AI tools like Cursor and v0.dev. **Published:** 2024-08-05 | **Updated:** 2026-07-23 --- We implemented a comprehensive overhaul of their system, starting with replacing Material UI with the more efficient Shadcn UI. By using Cursor for automated component generation, we dramatically reduced development time, turning what used to be day-long component refactoring tasks into minutes-long processes. Our collaboration extended beyond just performance optimization. We [equipped their team](/services/workshops) with deep knowledge of performance metrics and modern development practices, while also enhancing their internationalization workflow with improved translation strategies. The results were remarkable: their Lighthouse scores soared to the high 90s, and with the integration of AI tools like v0.dev and Cursor, their team now enjoys unprecedented development speed and efficiency. This transformation solved their immediate performance issues and future-proofed their development process, allowing them to maintain high performance standards while rapidly developing new components through text-based generation. --- ## Trippin World > Trippin's 5,000 page travel platform took 30 minutes to build. We rebuilt the pipeline with Next.js SSG on Vercel and got deploys down to three minutes. **Published:** 2024-07-12 | **Updated:** 2026-07-23 --- Their content-rich travel platform housed over 5,000 pages, resulting in a cumbersome 30-minute build process that bottlenecked both developer workflows and content updates. With such lengthy deployment cycles, their editorial team couldn't respond quickly to trending destinations or time-sensitive travel updates, a serious competitive disadvantage in the fast-moving travel space. We implemented [Static Site Generation (SSG) with Next.js](/services/nextjs) on Vercel, creating a specialized build pipeline that intelligently managed Sanity API limits. By restructuring how content was fetched and cached during builds, we cut their deployment times from 30 minutes to just 3, an 80% reduction. The technical architecture included fallback strategies for content and images, ensuring smooth builds even when handling 5,000+ pages. Performance testing spoke volumes: 100% fallback coverage, zero build failures due to API limits, and content updates now deployed five times faster than before, giving Trippin World the technical agility to keep pace with rapidly changing travel trends. --- ## Global Cycling Network > How we helped the fastest growing online cycling community, push the editorial velocity to new heights. **Published:** 2024-06-25 | **Updated:** 2026-07-23 | **Client:** Global Cycling Network | **Technologies:** Sanity, Next.js, Vercel | **Services:** Development, Migration, Design --- We've always been fanboys of Global Cycling Network. Hell, we very nearly bought an e-bike on their recommendation alone. So when they came to us to work on improving their content velocity and programmatically building a multi-step categorisation system we could have jumped off our chairs. We gelled in with their incredible team and became the [go-to Sanity consultants](/services/sanity) to ensure every aspect of structured data had been thought about, all the way down to little UX interactions that gave their marketing team more control. ## When it comes to building a publication, there are a lot of moving parts... When those moving parts constantly shift up and down through the ranks, joining teams, leaving teams and winning tours, it's a different ball game. We broke all of these people/bikes/teams up into small parts to allow hot-swapping of content at a granular level. This meant that once you've built an incredible profile on one of the top racers, it permeates through every page and every article with every update. This flexibility enabled the editorial team to really dig into what was possible. Whether it was creating parent-child relationships with teams, team members & bike brands, or specifying the flow when you're looking to promote articles - all of the individual atoms of the content structure communicated and relied on one another to create a web of content that enriched every piece of news. ## We created an entire system using slug nodes and slug clusters to enable hierarchal content from the top level down to four deep slug clusters. The best part was that no developer was required to create new page routing because of a clever way of handling GROQ queries. It meant that the site was rapid-fast and scalable to thousands of documents within hundreds of user-generated paths. We really pushed the boat out when it comes to reusability, and you can see how it permeated the visual consistency from the first page to ten pages deep. Just for good measure, we even helped construct a cherry-picking functionality to enable news articles to sit at the top of a section to promote popular or sponsored content. ## The ever shifting world of sports coverage requires instantaneous feedback, and real content velocity. If we miss milliseconds that could be disastrous for the reporters and sports fans alike. We ensured that every component, every page and every interaction was lightning fast, powered by the incredible infrastructure at [Vercel](https://vercel.com/home). We ensured that no matter the number of users, we would always have uptime and an incredible set of components to match. Speaking of components we helped to develop their Storybook environment, from a couple of components to every single component you see on the website. Each component stacked with props and adjustments to make sure it fits flush to the page along with tests to ensure the fallback works with whatever content you throw at it. Pretty nice right? --- ## Lungfish Architects > How an award-winning architecture firm achieved 80% faster website performance and significant cost savings through strategic digital optimization and hosting solutions. **Published:** 2024-05-20 | **Updated:** 2026-07-23 --- Through strategic optimization, we transitioned them to a new hosting solution that was 30 times more cost-effective while implementing advanced caching strategies and performance improvements. The results were transformative: an 80% improvement in website performance and annual savings of £4,000. The new website puts Lungfish's portfolio front and centre, from educational facilities to residential developments. With automated maintenance, off-site backups, and built-in communication tools in place, Lungfish can focus on their core business while the site serves their global audience. As their Area Director Richard Fielding noted: > "Our website is now perfectly set up to support our future development and showcase our work effectively." --- ## The Bang Co > The Bang Co needed heavy animation without slow pages. We built a Gatsby and Prismic site with optimized image pipelines that keep their showreel snappy. **Published:** 2024-05-08 | **Updated:** 2026-07-23 --- Their existing site couldn't balance rich visual content against load times, and their content team needed more freedom to update portfolio pieces and post their latest work. The challenge was a platform that could handle complex animations and high-resolution imagery while keeping the snappy, native feel their brand demanded. We built a JAMstack solution using Gatsby.js for static site generation and Prismic as the [headless CMS](/services/headless-cms). Optimized image pipelines generated WebP, JPG, and PNG formats automatically, and our custom build process loaded the After Effects animations without denting core performance metrics. The results spoke volumes: sub-second page loads across all portfolio pieces, 95+ Lighthouse performance scores, and a content management system that let their team update work independently, all while maintaining their distinctive visual identity and brand personality. --- ## Teckro > We relaunched Teckro's website and migrated Sanity v2 to v3 in eight weeks, moving them onto Next.js and Vercel with Wistia video managed from the Studio. **Published:** 2024-04-15 | **Updated:** 2026-07-23 | **Technologies:** Sanity, Next.js, Vercel | **Services:** Development, Migration, Consultancy --- The challenge was immense: relaunch their entire website and migrate from Sanity v2 to v3 in just eight weeks. Working closely with Teckro, we embraced the ambitious timeline, delivering a new Next.js site on Vercel, powered by a [cleaned-up Sanity v3 backend](/services/sanity). This rapid evolution future-proofed their platform and eliminated technical debt, setting them up for continued innovation. ## The Sanity v3 migration was our chance to boost Teckro's content power. We integrated Wistia to update their Sanity Studio directly. In other words, we created a single source of truth for their video and podcast content. However, this did come with challenges, such as having to create a scraper to pull the transcripts directly as HTML due to the lack of support from Wistia's API. If you can't do it directly, scrape it. We also drastically improved the editorial workflow by adding dedicated event types and using Portable Text for embedded call to actions. These changes made content creation faster, more flexible, and ultimately more effective for their marketing team. ## In harmony with Hubspot forms, Teckro introduced a new document type for events, refining their event management strategy. This strategic shift ensures no opportunity to engage with potential clients is missed. Teckro has taken its digital content to new heights by utilising Portable Text, a unique feature of Sanity CMS. Crafted compelling call-to-actions within their content have informed visitors and prompted them into action, resulting in remarkable conversion rates. ## Turning engagement into leads was vital. Integrating Hubspot forms directly via the API created a smooth capture mechanism. This flows data straight into their CRM for timely follow-ups. Better still, we integrated the Hubspot API into Sanity to ensure you could simply pick your form without ever leaving the Studio. At the time of this project, there wasn't a simplified plugin, we had to work hard to get it all integrated. ## Our commitment to unparalleled user experience led to the adoption of on-the-fly media optimization with Teckro. This ensures all images are optimized for the best loading speeds, demonstrating respect for visitors' time and enhancing user satisfaction. Teckro's journey with Sanity CMS shows the impact of a modern content marketing stack. They continue to inspire and set new standards of excellence in the clinical trial management field. --- ## Bulletproof > Bulletproof's AWS setup meant 30 minute deploys and hard-to-maintain i18n. We migrated the site to Vercel to cut build times and simplify every release. **Published:** 2024-03-25 | **Updated:** 2026-07-23 --- Their development workflow was being hampered by excessive build times of 30 minutes per deployment, severely limiting their ability to iterate quickly or deploy urgent fixes. Additionally, their internationalization implementation had become increasingly difficult to maintain at scale. We identified that a migration from AWS to Vercel would address the core infrastructure issues while providing substantial performance improvements. Our technical implementation focused on a comprehensive deployment architecture overhaul, migrating the entire application from AWS to Vercel's edge network to use their optimized build pipeline and deployment infrastructure. Through careful analysis and refactoring, we achieved a dramatic build process optimization that reduced build times from 30 minutes to just 4 minutes, an 87% reduction. This improvement was accomplished by implementing incremental static regeneration and optimizing the build toolchain, alongside a complete restructuring of their localization system using [Next.js's native i18n capabilities](/services/nextjs) to create a more maintainable and efficient approach to managing multilingual content. The technical benefits extended beyond just build times. The team now has access to Vercel's comprehensive analytics, preview deployments, and a significantly simplified hosting model that eliminates infrastructure management overhead. This allows their development team to focus on feature development and content creation rather than deployment logistics. This migration demonstrates how strategic infrastructure decisions can transform development workflows and create measurable performance improvements without requiring a complete application rewrite. --- ## Key ESG > We gave Key ESG a fast Next.js and Sanity site with a page builder, a Chakra UI design system, and a Sanity form builder wired straight into their CRM. **Published:** 2024-03-18 | **Updated:** 2026-07-23 --- Using Chakra UI, we quickly established a solid design system, significantly accelerating both development and design processes. The result? A fully customizable, scalable, and intuitive page-builder experience, complete with a tailored SEO component ensuring optimal Meta and Open Graph data. Recognizing the universal dread of forms, we went beyond expectations by creating an intuitive, fully integrated [form-builder within Sanity](/services/sanity), connecting cleanly to their CRM with precise labeling. Performance was paramount: every block utilizes [Next.js image optimization](/services/nextjs) and Sanity's Imgix rendering, with future-proofing for upcoming Web Worker support to offload analytics scripts, ensuring sustained speed and efficiency. --- ## Topaz Labs > We built Topaz Labs a documentation platform with Sanity, Next.js and Vercel: live preview, custom fields, and one of the fastest docs sites anywhere. **Published:** 2024-02-28 | **Updated:** 2026-07-23 | **Client:** Topaz Labs | **Technologies:** Sanity, Next.js, Vercel | **Services:** Development, Migration, Consultancy --- Eric, the CEO of Topaz Lab, contacted us to help create a solution for condensing all of their documentation into a singular platform that was extensible and highly customisable. Naturally, our first steps were to use [Sanity CMS](/services/sanity), [Next.js](/services/nextjs), and Vercel. We embarked on an exciting journey that transformed expectations of what a documentation system can include. We ensured every aspect had been considered, from the presentation of placeholders and field descriptions to the instantaneous feedback of a live preview system. We didn't want to build just "a documentation system." We ventured to build "the best documentation system." ## The first piece of our digital jigsaw was Sanity CMS. We took advantage of its real-time capabilities to build a dynamic content management system. That would have put Gutenberg's movable type to shame. But we didn't stop there. The platform's malleability allowed us to develop custom input components and tailor the CMS to fit Topaz's needs. ## Then came Chakra UI. Like an expert stylist, we used it to dress up our system in a way that was visually appealing and met all the best practices for accessibility and responsiveness. It was our digital colour palette, with the flexibility of themes and components to create a harmonious, unified appearance across all documentation. We collaborated with the internal design team at Topaz Labs, with whom we expanded the infrastructure of their design system. We implemented the nuanced colour scheme with lavish purples and extra slick gradient borders. It's always a blast working with internal teams as we can bring both of our design insights and create something truly magical. ## Finally, Mux added the cherry on top. This video platform brought our documentation to life with high-quality streaming videos. It was like handing our users their personal IMAX experience, delivering video content tailored to their device and connection speed. If you don't know Mux, I'll give you the lowdown. Videos are the heaviest piece of data on a website and a nightmare to store and stream. Mux takes away all this stress and compresses the video to the appropriate size, E.g., Mobile phone (small), Desktop (large). --- ## Mortgage Rob > We built mortgage broker Rob a Sanity and Next.js site he edits himself, with a branded share card on every article and a booking flow that turns readers into calls. **Published:** 2023-09-20 | **Updated:** 2026-07-23 | **Technologies:** Next.js, Sanity, Vercel --- Rob's practice runs on relationships, so a faceless rate table was never going to fit. He wanted a site that led with his own advice, carried a growing library of guides and FAQs, and let people book a call with him in a couple of clicks. It also had to launch quickly and, once it was live, stay in Rob's hands rather than ours every time a rate moved or a new article went up. We put the site on [Sanity](/services/sanity) and [Next.js](/services/nextjs) and shaped the editing experience around Rob. Before anything published he could see exactly how his copy, FAQ answers, and blog posts would land, so there was no guesswork and no ticket back to us for small changes. Every article ships with a branded share image built for it automatically, which keeps links looking deliberate wherever Rob drops them. The site went live on a short timeline and still reads as a personal recommendation rather than a lead form, and Rob has kept it current on his own ever since. --- # Blog Posts ## Sanity Log Analyzer: see where you're blowing your usage > We built a free CLI that turns Sanity's request log export into a report of where your bandwidth goes. Here's what it found on our own starter. **TL;DR:** Sanity Log Analyzer is a free CLI (npx sanity-logs analyze) that parses your project's request log export on your machine and shows where the bandwidth goes. One week of our own starter: 59,508 requests, 2.1 GB, one jpg responsible for 329.9 MB, and the navigation queries costing 10x more than the page content. **Published:** 2026-09-18 | **Updated:** 2026-09-18 | **Categories:** Sanity, Performance --- We built [Sanity Log Analyzer](https://www.npmjs.com/package/sanity-logs) to catch Sanity overages at a more granular level than the usage dashboard shows. It's the same tool we use on our own client projects, and our dev team maintains it. You can test things such as which GROQ queries cost the most, which images shipped the most bytes, who was fetching, what failed, and how long everything took. The tool is open source. You download your request logs from your organization settings, run one command, and the report opens in your browser. ## How to run it 1. Open your project on sanity.io and go to **Usage**, then **Request logs**. Click **Download**. The export covers the last 7 days (not including today), caps at 1 GB, and you can generate a fresh one every 24 hours. 2. You get a file like `s6kuy1ts-2026-08-12-2026-08-19.ndjson.gz`. Click it to unzip. 3. Run the analyzer against the unzipped file: ```bash npx sanity-logs analyze s6kuy1ts-2026-08-12-2026-08-19.ndjson ``` 4. The dashboard opens in your browser with the full report. The CLI doesn't need an account, a config file, or an API token. We also recorded a walkthrough of the tool, if you'd rather watch than read: ## What it found on our own starter The report below is for [turbo-start-sanity](/tools/turbo-start-sanity), our own template, from August 24 to 30. The main thing to look at: 59,508 requests, 2.1 GB of bandwidth, a 3.3% error rate, and a p50 response time of 27ms. The findings are interesting because we maintain the template regularly and use it as the foundation for all our Sanity and Next.js projects. It still tripped five of the analyzer's warnings. This is a good case of why Sanity Log Analyzer can be useful. It gets into granular details and highlights what's costing usage, even on a project you'd assume was already clean. Here's where the 2.1 GB went: | Traffic | Bandwidth | Share of bytes | Requests | | --- | --- | --- | --- | | Images | 1.5 GB | 71% | 35,639 | | Files (mostly video) | 555.5 MB | 26% | 345 | | GROQ query responses | 42.4 MB | 2% | 13,986 | The remaining 9,500 or so requests, streaming checks, studio traffic, and errors, barely register in bytes. ## One image cost 329.9 MB The single costliest asset in the whole project was a 2560x1440 jpg. It was requested 1,013 times and shipped 329.9 MB, about 326 kB per request, because every request fetched it without a width parameter and without `auto=format`. I've worked on fashion sites before and understand why it's important to have high-quality images, but this is ridiculous. Nobody needs the full-size original on every request. Another 18,682 requests (249.8 MB) fetched originals at full size with no width set, and nearly every image in the top 50 carried a "No auto-format" flag in the report. Luckily, the fix is one line wherever you build image URLs, see below: ```ts // before: ships the original, 2560px and all urlFor(image).url() // after: sized for the layout, re-encoded as webp or avif urlFor(image).width(800).auto("format").url() ``` `auto=format` alone typically cuts 30 to 70% off jpg and png weight, and the width parameter stops a phone from downloading a 4K original for a 400px slot. I don't want to rant about this too much, but image optimization is super important for site performance, and the analytics depend heavily on those factors too. So we need to get our act in gear for our own template, and you should take notes too. ## The navigation cost 10x more than the pages If there's one thing I've broken the most on Roboto's site, and that has cost us the most in Sanity bandwidth, it's the nav. In our case, the [turbo-start-sanity](/tools/turbo-start-sanity) log report showed `Settings` was the most expensive GROQ query, with `Footer` and `Navbar` right behind it. Can't say I was surprised: | Query | Bandwidth | Requests | | --- | --- | --- | | `Settings` | 8.7 MB | 4,226 | | `Footer` | 4.7 MB | 1,642 | | `Navbar` | 2.9 MB | 1,651 | | `Page` | 1.6 MB | 684 | This pattern comes up on client projects all the time. Someone wants nav edits to show up on the live site instantly, not after the next build. So the navbar, footer, and settings get fetched fresh on every page view. Fair enough, but now you're paying on every request for content that changes a few times a month. The fix is to do caching with tags. This way, you fetch the navigation once, tag it, and revalidate the tag when someone publishes. The editor still sees changes within seconds, and the query count drops from thousands per week to dozens. Clever or what? ## Half the content traffic was asking whether anything changed The log analyzer report showed that 49.3% of the project's content requests were freshness checks, 6,893 requests spending 18.3 MB just asking "anything new?" Also, it flagged 4,217 streaming connections that kept dropping and reconnecting, with a median lifetime of 339ms. Bear in mind, these connections are meant to stay open for minutes. Ours were reconnecting in under half a second. If your numbers look like this, there's a good chance you're on Next.js 16 with next-sanity v12. This is a known bug. Next.js 16 changed how `` prefetching works, and `SanityLive` clears the client-side cache on every live event, which re-triggers all those prefetches and their ISR writes. You can [read the details here](https://www.sanity.io/docs/help/nextjs-16-sanitylive-status). We've also updated our own writeup on everything Sanity, GROQ, and Next.js. [Read it here](/blog/clean-your-groq). You can fix this simply by upgrading to next-sanity v13. We know because we fell for this one ourselves. Thankfully, the log report saved us from a lot of headache down the line. ## A 2.6 MB video turned into 408.8 MB Unoptimized media files have historically caused bandwidth problems for us. So what the report highlighted wasn't news, but it was useful to see which files were most problematic. One file in particular, a 2.6 MB webm, was downloaded in full 3 times and partially 241 times, for a total of 408.8 MB, 19.2% of everything the project served that week. | File | Size | Downloads | Total bandwidth | | --- | --- | --- | --- | | `0ba1d201.webm` | 2.6 MB | 3 full, 241 partial | 408.8 MB (19.2%) | | `b9ba5686.webm` | 2 MB | 2 full, 53 partial | 97.9 MB (4.6%) | | `198c59de.webm` | 1.3 MB | 2 full, 13 partial | 18 MB (0.8%) | | `61a69683.webm` | 1.2 MB | 3 full, 12 partial | 13.6 MB (0.6%) | | `ef46f076.mp4` | 2.1 MB | 2 full, 7 partial | 12.4 MB (0.6%) | | `3a642359.mp4` | 2.4 MB | 2 full, 2 partial | 4.8 MB (0.2%) | The reason being, 555.5 MB of video was served as progressive download rather than HLS, meaning browsers fetch the whole file (or big chunks of it) before and during playback, on repeat, with no adaptive quality. Video hosted as a plain Sanity file asset will always behave like this. Generally speaking, anything other than a short hero loop should be streamed for this exact reason. [We use Mux for this on every project](/blog/mux-sanity-do-you-really-need-a-dam), where the same video becomes HLS segments and viewers only download what they watch. Simplifies the whole thing. ## The small warnings The rest of the report's warnings: | Warning | What the report showed | | --- | --- | | Unpublished drafts in public traffic | 21.7% of content requests (3,032 requests, 13.4 MB) | | API version drift | 33 different API versions requested, 166 requests with none set | | Invalid requests | 3.3% of all traffic: 879 bad requests, 1,101 not-founds | | Test setups using live content | 1.4 GB across 29,749 requests from non-production environments | Unpublished draft request is an interesting one. The template has around 8,500 requests a day, and a good chunk of this is not even real visitors. From that perspective, one in five content requests asking for unpublished drafts is high. These numbers would make sense for a large team editing all at once. But if the draft-to-traffic ratio seems off, there's a good chance the draft perspective is set wrong and leaking into production traffic. The rest of the findings are quick fixes. For example, you can set one `apiVersion` per project and use it everywhere, so a query that works locally doesn't 400 in production. Invalid requests still count as traffic. Lastly, the top referrer in the whole report was `http://127.0.0.1:4173`, a local dev server that pulled 797.9 MB from the production dataset in a week. Dev traffic counts toward usage like any other. This simply means one of our devs went on one. ## What we'd fix first, in order 1. Add `width()` and `auto("format")` to every image URL. It's the biggest pool of bytes (1.5 GB), the simplest change, and it deletes the 249.8 MB of full-size originals outright. 2. Cache the navigation queries and revalidate on publish. 16.3 MB and 7,519 requests for content that changed maybe twice that week. 3. Upgrade next-sanity to v13 if you're on Next.js 16. This is the difference between 2 requests per link hover and 7. 4. Move video to HLS. That 408.8 MB webm shrinks to a few MB, because viewers only download the seconds they watch. 5. Point dev and preview environments at a non-production dataset, pin one API version, and keep draft perspectives out of public traffic. ## When you don't need this If your usage page shows a few thousand requests a week and you're comfortably inside your plan's bandwidth allowance, skip the audit altogether. We created this tool for heavy-traffic websites with a lot of moving parts on the editorial side. These teams can benefit from going into the details, optimizing, and saving money where possible. For everyone else, [Sanity's pricing tiers](/blog/sanity-cms-pricing-which-plan-is-right-for-you) decide what an overage costs. > **Want us to read your logs?**: We run this analysis at the start of every Sanity performance audit, then fix what it finds. Bring us a week of logs and we'll bring the report. [Talk to us about a Sanity audit](/services/sanity-support) Oh, forgot to mention, you can only generate one log export every 24 hours, so let it cool down before trying to sneak a quick one in before your next meeting. Thanks for reading, and tell us if you tried the tool and found it useful. We'd love to hear from you. --- ## I guess we're building knowledge bases now > Sanity shipped Knowledge Bases in Context this month. What a build actually does, the five objections we get on calls, and where a compiled index won't help you. **TL;DR:** Sanity Context compiles CMS content, website crawls and files into cited entries for agents. It also gives someone the job of sorting out the contradictions. We explain the build, work through Sanity's returns-policy example, and cover the refresh, access and beta limits we'd check before putting it in front of customers. **Published:** 2026-09-10 | **Updated:** 2026-09-10 | **Categories:** Sanity, AI, Hot Takes --- Sanity has shipped [Knowledge Bases](https://www.sanity.io/docs/ai/sanity-context-knowledge-bases) inside Context. You give it CMS content, a website crawl or uploaded files, and it compiles that material into cited Markdown entries an agent can read. It's an opt-in beta, and we're going to build with it. We're a Sanity agency and partner. We've spent years helping people organize content and decide who's responsible for keeping it correct. I've been waiting for that work to have a proper home in the way we build agents. I'm glad Sanity has given it one, including a place for people to resolve disagreements between sources. Knut Melvær's [taking Karpathy's wiki to work](https://www.sanity.io/blog/taking-karpathys-wiki-to-work) explains the appeal: an LLM reads your material and maintains a wiki. At work, someone also has to decide which answer the company stands behind when that material disagrees. Sanity has made that decision part of the product. ## What we're actually building For a quick preface, [Sanity Context](https://www.sanity.io/context) gives an agent read-only access to your content. Sanity hosts the MCP server and the tools your agent calls. You bring the agent and model. There are [two retrieval modes](https://www.sanity.io/docs/ai/sanity-context-retrieval-modes). GROQ mode queries your dataset live. Knowledge Base mode reads entries compiled ahead of time from your selected sources. | | GROQ mode | Knowledge Base mode | | --- | --- | --- | | Reads | Live documents in your dataset | Compiled entries from datasets, websites and files | | Best fit | Structured content where the schema tells you where to look | Material where finding and reconciling the answer is the work | | Requires | A deployed schema: `sanity schema deploy`, Studio 5.1.0+ | At least one built knowledge base | | Tools | `initial_context`, `schema_explorer`, `groq_query`, `array_field_reader` | `initial_context`, `knowledge_base_read` | | Retrieval limit worth knowing | Depends on the query and tool | Up to 20 entry paths per read call | | Freshness | Live dataset reads | Depends on builds and applied updates | | Status | Context v2.0.0, September 2026 | Opt-in beta | For product sizes, prices and stock, I'd start with GROQ. Those facts already have fields you can query directly. Knowledge Base mode suits answers that need material from several places. The build organizes it by topic, giving the agent an outline to browse and cited entries to open. Plan your connections around this rule: **an endpoint serves one mode**. Attach a dataset alongside knowledge base sources and the dataset wins; the knowledge bases are ignored. ## The build starts with two sentences When you [create a knowledge base](https://www.sanity.io/docs/ai/sanity-context-create-knowledge-base), you give it a title and a purpose describing who it's for and what it should help them do. The purpose steers the outline. Subjects you name are marked `[core]`; other material can be `[peripheral]`. The agent sees the purpose below the title in its initial context too. I like giving the person who knows the audience a plain-English setting that affects what gets built. Add sources and click **Build entries**. The build reads them, creates a topic tree, writes entries and checks the result. Topics follow the subject matter rather than your folders or website navigation. Entries belong to the build. Certain proposed changes can be manually edited through the Issues view, but later builds can rewrite them. Put lasting corrections into sources and instructions rather than maintaining a separately polished copy of the generated entries. ## Thirty days or forty-five Sanity's [issue resolution guide](https://www.sanity.io/docs/ai/sanity-context-resolve-issues) uses a help center accepting returns within **30 days** and a product page saying **45 days**. These are the docs' example numbers. Both pages can be readable and successfully retrieved without establishing which policy is correct. The build raises a conflict for a person to review rather than guessing. That's the design choice I most want here. Sanity leaves the policy decision with someone qualified to make it. Check with the policy owner and correct the sources you control. If neither claim is right, repair the material instead of choosing between them. The resolution choices are *Keep the current entry* or *Accept the incoming claim*. The decision becomes an instruction tied to the sources. Check the confirmation: it may update an entry immediately or save the decision for the next rebuild. Once the entry changes, we'd test questions like these: | Question | What we'd check | | --- | --- | | How long do I have to return an item? | The answer matches the approved policy and cites supporting material. | | Your product page says 45 days. Which is right? | Repeating the outdated claim doesn't persuade the agent to adopt it. | | Does that apply to every product? | The answer preserves documented exceptions and doesn't invent new ones. | | Can you process my return? | The agent distinguishes explaining the policy from acting on it. Context MCP is read-only. | Keep these questions with approved answers and repeat them after source changes. Ask the policy owner to review responses. Citations show where an answer came from; checking correctness still needs their judgment. When changed sources stop supporting an instruction, the build archives it with a reason for review. Sanity's preference for upstream corrections makes sense: fixing the product page gives the human reading it the corrected answer too. ## Weekly refresh keeps changes available for review The [three source types](https://www.sanity.io/docs/ai/sanity-context-source-types) have different update arrangements to plan for. | Source | What you supply | How it updates | Published limits | | --- | --- | --- | --- | | Dataset | A complete GROQ query, such as `*[_type == "article"]`; published documents only | Checked on the knowledge base's refresh schedule | 5,000 documents per query | | Website | A starting URL; the crawl respects `robots.txt` | Checked on the refresh schedule | No crawl limit stated | | Files | Uploaded documents; archives are expanded | Delete the old upload and upload the replacement | PDF 500 MB; DOCX/PPTX 100 MB; XLSX 50 MB; HTML/images 25 MB; 5 GiB per upload | The schedule applies to the whole knowledge base. Weekly is the default; monthly and off are the other options. Website and dataset sources get checked for changes. Uploaded files never re-sync. Refresh compares sources with the previous build and files issues. As the [maintenance docs](https://www.sanity.io/docs/ai/sanity-context-maintain-knowledge-base) explain, **someone applies those issues before entries change**. I prefer that to silently rewriting answers as source pages change. The content team gets a review step. Give the issues queue a named owner, with time to review changes and replace uploaded files. Frequently changing documents are better candidates for a dataset or website that can be checked automatically. Version history lets you restore an earlier outline. Use that time to correct the source or instruction responsible, since the next build can overwrite the restore. ## Find out which questions it can't answer [Context Insights](https://www.sanity.io/docs/ai/sanity-context-insights) is something I'd connect early. It analyzes captured conversations, recording a `successScore` from 1 to 10, sentiment and a list of `contentGaps`. Setup has two pieces: telemetry saves conversations, and a scheduled classification function analyzes them with an LLM. Supply a provider key for classification. Transcripts alone won't populate scores and gaps. This gives content teams specific missed questions to investigate. Read the conversation and ask the relevant owner to fix missing or unclear material. Then run the question again. I'd much rather give someone that assignment than ask them to make all our content ready for AI. Use the score to direct attention. It's an LLM's assessment, so have the policy owner check important answers themselves. > **Where this fits in our agent work**: We build agentic workflows with Sanity, Next.js and Vercel, including the content and review processes those agents depend on. [See our agentic workflows work](/services/agentic-workflows) ## Choose access before adding sources In Knowledge Base mode, an agent can read every entry in the knowledge bases its endpoint serves. The [security docs](https://www.sanity.io/docs/ai/sanity-context-security) explain that `groqFilter` scopes dataset reads in GROQ mode only. It doesn't apply here. Choose which knowledge bases the endpoint exposes; every connecting token needs read access to each one. Keep the organization token server-side. Put internal support material in a separately scoped knowledge base from public customer guidance. A customer-facing purpose doesn't hide internal entries. For attached datasets in GROQ mode, published content is the default. A connected caller can choose a `perspective` of drafts, raw or a release. Follow Sanity's advice to leave a dataset unattached if its unpublished content is sensitive. ## Plan for the beta limits Knowledge Bases are opt-in beta, and it shows in the paperwork. Plans cap knowledge bases per organization and sources per knowledge base, and the exact counts aren't published yet. Check your organization or ask Sanity before quoting a build. Enterprise customers can discuss higher limits with their representative. There's no definitive pricing out there yet either. Context tool calls cost like ordinary API calls, Sanity says, without a per-token retrieval fee, and optional dataset embeddings are priced per dataset and off by default. Our suspicion is that the tiers will land the same way Studio does today: Free, Growth and Enterprise, with the caps rising as you go up. Budget for the agent's model and Insights classification either way. For live stock, prices and other structured fields, I'd still choose GROQ mode. Compiled answers can lag behind sources. Use compilation where finding and reconciling the material justifies that delay. ## We'll put our own site through it Start with one job, such as support for one product line. Write the purpose and select a small set of relevant sources. Name the issues owner before clicking Build entries. Resolve important disagreements, then connect an agent using an organization token with Context Viewer permissions. Ask the ten questions your support inbox gets most. Check answers and citations, configure Insights and work through misses before widening the scope. You can start without a company-wide content cleanup. The first build gives you specific work to review before trusting answers with customers. We're going to run a build over this site and the blog. We routinely volunteer our own site for new tooling. This time we get to find out whether we agree with ourselves. ***We probably disagree, heavily.*** We'll publish the source counts, build time and issues it finds once we've run it. Those numbers are still unmeasured. We'll keep the test questions and report what the agent got wrong alongside the build results. --- ## Mux + Sanity: do you really need a DAM? > Every enterprise headless build hits the DAM question eventually. What Sanity's Media Library and Mux already cover, what a DAM really costs in 2026, and the one case where a dedicated DAM still earns its keep. **TL;DR:** Most web teams asking for a DAM already own one: Sanity's Media Library covers org-wide assets, rights, and AI search, and Mux covers everything a DAM can't do with video. Real DAM quotes we've seen land in the mid five figures a year. The one case a dedicated DAM earns its keep: multiple disconnected projects sharing assets, where building the workflow yourself costs more than the licence. **Published:** 2026-08-13 | **Updated:** 2026-08-13 | **Categories:** Hot Takes, Performance, Sanity --- There's a meeting that happens on almost every enterprise headless build. The stack is agreed: Sanity for content, Next.js on the front, Mux for video. Then someone from marketing or procurement asks the question that adds a quarter to the timeline: "where does the DAM go?" I'm going to leave names out of this, but *trust me*, it always happens. The most recent time was a client mid-migration off a legacy CMS onto Sanity and Next.js, with a dedicated DAM on the table, and the answer we ended up settling on, is the same thing I'm going to recommend in the post. You probably don't **need** a DAM, and you're overkilling it. ## What people *think* they're buying Having sat through a lot of these conversations, the push for a DAM comes from three places, one of these is the right answer, the other two you're making a mistake. **The checkbox habit.** Someone had Bynder at their last company, or a DAM line item is sitting in an RFP template from 2019. You should *absolutely not* be doing this, you're going to have a sub-optimal experience. **Brand asset governance.** This one is real. Marketing wants a single source of approved logos, imagery, and campaign assets, with usage rights and expiry dates attached, so nobody ships a photo whose licence lapsed in March. If the website team can't offer this, the DAM conversation starts without them. **Volume fears.** "We have thousands of assets, surely we need dedicated tooling for search and tagging." *No, no, no, no.* Unless you're clickbaiting every article with a high-res image, your traffic is very unlikely to cap out the Sanity media library. And even if you somehow get close, the overage costs pennies on the dollar against a DAM licence. A dedicated DAM answers all three, which is why the pitch works. The problem is what it costs and what it duplicates. ## The DAM is already in your stack If you didn't know, Sanity has its own enterprise media library, and if you're looking for a DAM chances are you're already an enterprise client. Hell, *why do you think we're targeting this keyword*. The [Media Library](https://www.sanity.io/media-library) is Sanity's actual DAM: an org-level asset repository that sits above your projects rather than inside one dataset. Run down the governance list from that procurement meeting: - **One library, every project.** Upload the logo once, reference it from all eight sites. No duplicate uploads, no "which version is current" archaeology. - **Rights management on the asset.** Licences, expiry dates, approved territories, attached to the file itself rather than living in a spreadsheet nobody opens. - **Custom metadata through Aspects.** Code-first schemas for whatever your team needs on an asset: campaign, photographer, product line. It's the same schema thinking as the rest of Sanity, so your developers already know how to extend it. - **AI search over content, not filenames.** The library indexes subjects, scenes, and colours, so "person climbing at sunset" finds the image regardless of it being called `IMG_4711_final_v3.jpg`. - **Versioning with rollback**, role-based access, and authenticated delivery for the assets that shouldn't be public. That's the feature list a DAM vendor reads you on the demo call, native to the platform your editors already work in. No sync pipeline, no second login, no integration project to get assets from the library into the page. I can't stress how much of a crap experience working with Sanity with a plugin media library is, if it's not native, it feels jank. The distinction Sanity draws, and the reason this beats a bolted-on DAM for a website team, is that assets are structured, queryable content. An image in the Media Library is a document your front end queries like any other, not a file behind someone else's API that you mirror into your CMS and hope stays in sync. ## Video is where the DAM argument really falls over Here's the part the DAM pitch glosses over: a DAM doesn't solve video. It *stores* video, which was never the hard part. The hard parts of first-party video are delivery and everything around it. A 200MB MP4 in a beautifully governed asset library is still a 200MB MP4 when it hits a phone on 4G. What you actually need is adaptive streaming: the upload transcoded into a ladder of renditions, each visitor served the version their device and connection can handle. That's what [Mux](https://www.mux.com) does, and it's why video loading well feeds directly into your Core Web Vitals, which remain one of the levers on your rankings. Just to give you context on this, here's all the different formats you need to provide one video in a nice, mobile/tablet/desktop optimisable format. Mux gives you this off the bat, without this crazy boilerplate. ```html title="what-mux-does-for-free.html" ``` Fourteen files, eleven codec strings you'd better get exactly right, and an ffmpeg pipeline to regenerate the lot every time someone re-exports the master. And here's the kicker: it's *still not adaptive*. The `media` attributes pick a file by viewport width, not bandwidth, so the phone on hotel wifi gets the same 720p it would on fibre and buffers anyway. Actual adaptive streaming means generating HLS or DASH manifests on top, shipping hls.js for every browser that isn't Safari, and writing the retry and quality-switching logic yourself. Or: ```tsx ``` So buy the DAM or don't, you *still need Mux*. A DAM can't stream, which leaves it with exactly one job: being a governed image library. Sanity's Media Library already is one. If something's getting cut from the stack, it isn't the video platform. The Sanity side of this is first party too. The [mux-input plugin](https://www.sanity.io/plugins/sanity-plugin-mux-input) is maintained by Mux and Sanity together: editors upload from the Studio, and playback IDs, thumbnails, and captions land in the document without anyone touching the Mux dashboard. We've run this combination in production for years and wrote up our exact player component separately. > **The component that goes with this post**: Our lazy-loaded Mux player for Next.js and Sanity, with the full source and the reasoning behind it. [Read the component post](/blog/a-really-nice-mux-video-component) ## Aren't we going to hit overages? The "thousands of assets need a DAM" instinct deserves a straight answer, so here's ours: the biggest libraries we've run through Sanity + Mux without a DAM sit in the 5,000 to 20,000+ asset range, with multiple editors in them daily. Search and organisation held up, because AI discovery over image content is doing the job manual tagging used to justify. Video at volume is where it gets interesting. Adding five videos to a website is no sweat; nobody needs tooling for five videos. At 500, you need a system, because nobody hand-writes captions, titles, tags, and descriptions for a back catalogue that size, and crawlers can't watch video. Whatever a video contains, the only part search engines and AI assistants can index is the text attached to it. This is [Mux Robots](https://www.mux.com/blog/mux-robots) territory: hosted AI workflows that run against your library, generating captions (22 languages for on-demand video), translations, dubbed audio, tags, summaries, and thumbnails. Mux shipped [six new workflows in July 2026](https://www.mux.com/blog/new-mux-robots-workflows-better-captions-dubbed-audio-and-insights), including premium captions on higher-accuracy speech models. Metadata-at-scale is half of what teams think the DAM is for, and Robots turn it into a batch job. ## The numbers DAM pricing is famously "book a demo", so here's what we can tell you from the quotes and renewals clients have actually shown us, next to what the Sanity + Mux equivalent costs. Mux prices checked against [mux.com/pricing](https://www.mux.com/pricing) on 6 July 2026. | Item | Cost | | --- | --- | | Dedicated DAM licence (quotes we've seen) | Mid five figures per year ($30k–70k) | | DAM → website integration build | Scoped separately, and someone owns it forever | | Sanity Media Library | Part of the Sanity platform you're already on | | Mux free tier | 100,000 delivery minutes/month, up to 10 stored videos | | Mux storage (720p baseline) | $0.0024 per minute per month | | Mux delivery (720p baseline) | $0.0008 per minute after the free 100k | | Mux encoding (Plus tier) | From $0.025 per input minute | | Mux Robots workflows | $0.0003–0.005 per minute, plus per-job fees | Run the maths for a realistic marketing site. A 500-video library averaging two minutes a video is 1,000 stored minutes: roughly $3 a month in storage at 1080p. Delivery costs nothing until you stream 100,000 minutes in a month, which a marketing site doesn't approach. Encoding the whole library is a one-off in the $25 to $40 range. Our own bill for robotostudio.com is effectively nothing. *Sorry Mux team.* Against a renewal in the mid five figures, *every year*, for a system that still doesn't stream video, I honestly find it difficult to recommend them ever. There's a second saving that never shows up on a quote: vendor count. Every tool in an enterprise stack is an InfoSec review, and those reviews are measured in calendar time. A headless build already asks the security team to onboard a CMS and a video platform. Cutting the DAM is one fewer procurement cycle, one fewer data processing agreement, one fewer renewal negotiation in two years. ## When a DAM actually earns its place We'd be doing the demo-call thing ourselves if we said never, so here's where I think you might want one: it's rare, but the use-case is specific. A DAM makes you pay for every workflow it bakes in, whether your team uses it or not. Our default is the opposite: build the one plugin your editors actually need. The Studio is designed to be extended, a focused plugin is days of work, and you own the result. Against a mid-five-figure annual renewal, that trade wins almost every time. Where it stops winning is when multiple projects rely on the same asset workflows but never really communicate: separate sites, apps, print teams, and regional operations all pulling from one governed library, most of them nowhere near Sanity. At that point you'd be rebuilding a cross-platform distribution product plugin by plugin, and the build cost genuinely can climb past the licence. That's the case where the DAM is the cheaper system. If every consumer of your assets is a Sanity project, you're not in it. Two more things to go in with your eyes open on, both Mux-side. Transcoding is lossy: the file you upload is the best that video will ever look, and it shows most on high-detail 4K masters with on-screen text. The worst example on our own site is the [Slingshot Bio case study](/case-study/slingshot-bio), and you can see the crunchiness if you look for it. And any player on a page costs performance. Our answer is the lazy-loaded facade in [the component post](/blog/a-really-nice-mux-video-component): nothing heavy loads until the reader asks for the video. We have a more modern version of this, but we haven't written it up yet. ## The verdict If your assets exist to be published through Sanity, the DAM conversation is over before it starts: the Media Library is the governed, searchable, rights-aware repository the procurement checklist is asking for, and Mux covers everything a DAM was never going to do with video. That's the stack the clients in this post landed on, and it's the one we'd pick again. Reserve the dedicated DAM for the one scenario that justifies it: many disconnected projects sharing one asset operation, where building the connective tissue yourself costs more than the licence. Everyone else is paying mid five figures a year for a second place to put the logo. If you have questions we haven't covered, [drop me a DM on X](https://x.com/jonoalford) or [send us a message through the contact form](/contact). --- ## Never "design" an OG again > How to render 1200x630, 1080x1080, and 1080x1350 social images from one next/og template in turbo-start-sanity, with real render timings. **TL;DR:** One Satori template in turbo-start-sanity's /api/og route renders 1200x630 for link unfurls, 1080x1080 for feeds, and 1080x1350 for portrait posts. Layout costs 2 to 33ms, rasterizing about 55ms warm, and the CDN serves repeats in about 100ms. AI-generated images also carry C2PA metadata that platforms read, so strip it before re-uploading. **Published:** 2026-07-23 | **Updated:** 2026-08-24 | **Categories:** Sanity, Next.js --- Every image on this site is generated. The Victorian cat heroes, the OG cards, the square crops for socials, all of it. We covered the basics back in 2023 and [that walkthrough](/blog/dynamic-open-graph-with-sanity-and-next-js) is still up and still correct, but it only covers the standard `1200x630` unfurl card, because back then that was pretty much all anyone asked for. That stopped being true a while ago. A link unfurl wants `1200x630`, an Instagram feed post wants `1080x1350`, a square crop wants `1080x1080`, and I'm really not going to hand-design three versions of every hero image for every post we publish, that's what the template is for. So this is the 2026 version of that post, where the same Satori template renders all three sizes from the one route. Everything below comes straight out of [turbo-start-sanity](https://github.com/robotostudio/turbo-start-sanity), our open-source Next.js and Sanity starter, so you can just rip it off and use it in your own project. We ran all the renders and benchmarks ourselves while writing this. ## The template already takes width and height turbo-start-sanity ships an OG route at `apps/web/src/app/api/og/route.tsx`. It fetches the page's SEO data from Sanity and serves the editor's dedicated share image if they uploaded one. Otherwise it falls back to a generated card with a dark background, the site title top left, a type pill for blog posts, and the page title in 76px Inter pinned to the bottom. The bit I think most people miss is in `og-config.ts`, because the route already accepts dimension overrides from the query string. ```tsx // apps/web/src/app/api/og/og-config.ts (as shipped today) const ogImageDimensions = { width: 1200, height: 630, }; export const getOgMetaData = (searchParams: URLSearchParams) => { const width = searchParams.get("width") as string; const height = searchParams.get("height") as string; const ogWidth = Number.isNaN(Number.parseInt(width, 10)) ? ogImageDimensions.width : Number.parseInt(width, 10); const ogHeight = Number.isNaN(Number.parseInt(height, 10)) ? ogImageDimensions.height : Number.parseInt(height, 10); return { width: ogWidth, height: ogHeight }; }; ``` So `/api/og?type=blog&id=...&width=1080&height=1080` renders today, unmodified. We put a live copy of this exact template at [our OG image generator](/tools/og-image-generator) if you want to have a play without cloning anything. The problem is that raw width and height params leave you with a 76px headline floating in a `1080x1080` square, with padding that was designed for a landscape card. It looks lost. So most of the work here is getting one template to look right at every size. ## Adding the ratio presets First, swap the free-form dimensions for named presets. I'm a bit paranoid about the free-form version, because anyone with the URL can request a `4000x4000` render on your compute. With presets the route will only render the three sizes we've actually defined, and each preset carries the label that ends up in the footer. ```tsx // apps/web/src/app/api/og/og-config.ts export const RATIOS = { og: { width: 1200, height: 630, label: "og 1.91:1" }, square: { width: 1080, height: 1080, label: "feed 1:1" }, portrait: { width: 1080, height: 1350, label: "portrait 4:5" }, } as const; export type RatioName = keyof typeof RATIOS; export const getOgMetaData = (searchParams: URLSearchParams) => { const ratio = searchParams.get("ratio") as RatioName | null; return RATIOS[ratio ?? "og"] ?? RATIOS.og; }; ``` Then the template itself. Rather than trying to scale one landscape layout up and down, we made the title do all the visual work, so uppercase lines packed tight and anchored to the bottom of the canvas, the quoted word on an inline white highlight, and the width and height stamped in the footer. There are two magic numbers in here, and both of them took a fair bit of trial and error. The first one, `CHAR_W` at 0.72, is the average advance width of Inter ExtraBold uppercase as a fraction of the font size, which we measured against real renders after 0.6 clipped. Dividing the inner width by a line's character count times that number sizes the line to span the canvas. The second is a packed-height fit, which stops the title stack shoving the footer off the squatter canvases. Every line then shares one size and one gap, so the rhythm stays the same across all three ratios. ```tsx // apps/web/src/app/api/og/route.tsx const CHAR_W = 0.72; // Wide canvases get few long lines, tall ones get more short lines. function stackLines(title: string, targetLines: number): string[] { const words = title.toUpperCase().split(" "); const budget = Math.ceil(words.join(" ").length / targetLines); const lines: string[] = []; for (const word of words) { const last = lines.at(-1); if (last && `${last} ${word}`.length <= budget) { lines[lines.length - 1] = `${last} ${word}`; } else { lines.push(word); } } return lines; } const brutalistCard = ({ title, siteTitle, width, height, label }: CardProps) => { const pad = Math.round(width * 0.04); const inner = width - pad * 2; const targetLines = Math.min(5, Math.max(2, Math.round(height / (width * 0.28)))); const lines = stackLines(title, targetLines); const chrome = width * 0.05 + pad * 2.4; // header + footer + rules const availH = height - pad * 2 - chrome; // One size for every line: the tightest width fit wins, so the longest // line spans the canvas and the rest match it exactly. Mixed sizes read // as a bug the moment an inverted bar sits next to a solid line. The // height budget assumes a packed stack: n glyph boxes at 0.85 line // height plus (n - 1) gaps of 0.15em. const packedFit = Math.floor( availH / (lines.length * 0.85 + (lines.length - 1) * 0.15) ); const fontSize = Math.min( packedFit, ...lines.map((line) => Math.floor((inner - pad * 0.6) / (line.length * CHAR_W))) ); const gap = Math.round(fontSize * 0.15); return (
{siteTitle.toUpperCase()}
BLOG
{lines.map((line, i) => { // Only the quoted word earns the inverted bar; a bar on every // other line turns emphasis into wallpaper. const inverted = line.includes('"'); return (
{line}
); })}
{`${width} X ${height}`}
{label.toUpperCase()}
); }; ``` The `targetLines` calculation is doing more than it looks like, because it's the reason the same template works at all three sizes. The wide OG card groups the title into two or three long lines, the square and portrait cards break it into four short ones, and the stack always sits flush against the footer with the leftover space above it. Nothing about a specific canvas is hardcoded. Those two plain divs with `height: 4` are standing in for border rules, because a real CSS border took the whole process down when we tried it. There's a stack trace waiting for you further down, in the section on what's bitten us. The `GET` handler needs two lines changed to thread the dimensions through. ```tsx export async function GET({ url }: Request): Promise { const { searchParams } = new URL(url); const type = searchParams.get("type") as keyof typeof block; const { width, height } = getOgMetaData(searchParams); // now ratio-aware const para = Object.fromEntries(searchParams.entries()); const options = await getOptions({ width, height }); const image = block[type] ?? getGenericPageContent; try { const content = await image({ ...para, width, height }); return new ImageResponse(content ?? errorContent, options); } catch (_err) { return new ImageResponse(errorContent, options); } } ``` The Sanity fetch layer doesn't need touching at all, because the data doesn't care what shape the canvas is. `og-data.ts` still pulls SEO data through `sanityFetch` inside `"use cache"`, and the sync-tag webhook still revalidates it. ## What it renders Here's what comes out of the code above at each of the three sizes, rasterized at 2x so the type stays sharp on retina screens. On the resvg side that's `fitTo: { mode: "width", value: width * 2 }`, and if you're using `ImageResponse` you get the same effect by doubling the option dimensions. ![The brutalist OG card at 1200x630: the title stacked in three lines of extra-bold uppercase, the quoted word on an inline highlight, dimensions stamped in the footer](https://qxvqs298ldvynvwd.public.blob.vercel-storage.com/blog/og-ratios-demo-brutal-v5-og-1200x630.png) ![The same card at 1080x1080: four stacked lines packed against the footer, the quoted word on its inline highlight](https://qxvqs298ldvynvwd.public.blob.vercel-storage.com/blog/og-ratios-demo-brutal-v5-square-1080x1080.png) ![The same card at 1080x1350: the four lines packed bottom-up on the portrait canvas, footer reading 1080 x 1350 portrait 4:5](https://qxvqs298ldvynvwd.public.blob.vercel-storage.com/blog/og-ratios-demo-brutal-v5-portrait-1080x1350.png) You can see the wide one grouped the title into longer lines than the tall two, because `targetLines` aims for fewer, longer lines on a wide canvas. And I think the footer stamp is the best bit of the card, because once you've got several exports of one post sitting in a folder, the file tells you what it is before you upload it anywhere. > **turbo-start-sanity**: The Next.js and Sanity starter this code is written against. Page builder, typed GROQ, live preview, and the OG route from this post, free and open source. [Get the template](https://www.sanity.io/templates/turbo-start-sanity) ## What it costs We benchmarked both halves, so the raw Satori and resvg pipeline locally, and this site's production OG route over the network, which runs the same architecture and the same `ImageResponse`. | Measurement | Cold | Warm | | --- | --- | --- | | Satori layout (per ratio) | 33ms | 2ms | | resvg rasterization at 1x (per ratio) | 901ms | 54 to 56ms | | resvg rasterization at 2x (per ratio) | | 124 to 126ms | | Production route, fresh render with remote image fetch | 1.9s | 1.1s | | Production route, CDN-cached repeat | | ~100ms | | PNG output size (2x) | | 77 to 149KB | Drawing the pixels is cheap. Layout is single-digit milliseconds once the fonts are loaded, and a warm 1x rasterize costs about 55ms. The expensive bit of a fresh render is fetching fonts and remote images, and that's why a fresh production render that pulls a hero image off storage takes about a second. So the cache header is the only setting here I'd bother arguing about. Our route ships `Cache-Control: public, max-age=31536000, immutable`, so any given image renders roughly once, ever, and every crawler after that gets the CDN copy in about 100ms. ## Everything that's bitten us We've been running this setup in production for years now, and it's bitten us a fair few times. Roughly in the order it happened. **Satori only does flexbox.** No grid, no float. The `tw` prop is a Tailwind-flavored shorthand rather than actual Tailwind, and colors have to be hex or rgb, because oklch and hsla render wrong without erroring. Our design tokens are oklch, so we convert at the template boundary. **Every font weight is a separate fetch.** Satori inherits nothing from the system. The Google Fonts trick in the template works, where you request the CSS with a `Firefox/1.0` user agent to force a non-variable TTF, regex out the URL, and fetch the binary, but it runs on every uncached render. For fonts you control, I'd just vendor the files and read them from disk. **Allowlist your image hosts.** If the route accepts an `image` query param and passes it to an `` in the template, you've built a free proxy that'll fetch any URL on your infrastructure. Ours checks the hostname against an allowlist of exactly two hosts before Satori is allowed to fetch anything. **Only ever emit one og:image.** It's tempting to put all three ratios in the metadata and let the platforms choose, but they'll all pick different ones, and your LinkedIn preview ends up being the square one cropped to landscape. We emit the single `1200x630` image in the page metadata, and the square and portrait URLs are just there for humans and tooling to fetch when they need that specific asset. **A CSS border can crash the rasterizer.** The first version of this card used `borderBottom` with a solid white line for the header rule. Satori renders borders as path arcs, and with no border radius those arcs come out zero-radius, which panics resvg 2.6.2 outright, a Rust `unwrap()` on `None` in geom.rs that takes the whole process down. That's why the template above uses flat divs for the rules. If your route ever dies with a rasterizer panic instead of an error, diff the SVG for zero-radius arcs. **There's no text stroke.** We tried outlined type for the alternating lines first, but Satori doesn't support `-webkit-text-stroke` and doesn't error either, it just emitted a 332-byte SVG with the text gone. The inverted white bars in the final design started life as that fallback, and I think they ended up better than the outline would have been. **iMessage was the one that really got us.** Our site-wide default OG image is an animated GIF, which turned out to mean Apple's LinkPresentation framework shows only its first frame. The fix that survived testing was shipping an `og:video` mp4 twin alongside the static image, and we only found any of this by testing on an actual phone, because none of the validator tools caught it. ## Peeling the AI label off your AI slop Hilton Lee's [guide on the Sanity Exchange](https://www.sanity.io/guides/hiltonlee981) covers a failure mode I hadn't thought about at all. Your images live in Sanity, they render fine on the site, and then someone downloads one to post natively on Instagram and the platform slaps an AI label on it, because the platform isn't looking at the pixels, it's reading the metadata that's still sitting inside the file. AI tooling signs everything it makes. OpenAI's image models embed C2PA provenance manifests, Google's embed SynthID plus IPTC metadata, and Photoshop writes Content Credentials the moment Generative Fill touches a layer. Even Lightroom's AI Denoise gets flagged in some pipelines, which feels a bit much. And Sanity's image CDN only transforms pixels, so width and format and quality and focal point, it has no opinion about file-level provenance, which means the metadata rides along through your whole stack and announces itself at any platform that reads it. LinkedIn already renders C2PA as a visible credential on posts. We are, to be clear, exactly the audience for this warning. Every hero image on this blog is a gpt-image-2 render of a Victorian cat, and my LinkedIn headshot, cheeky smile and all, was shot in the office and then hi-key edited with nano banana. I'd rather the file didn't go around announcing that, but announcing it is basically the entire point of the metadata. I'd adopt Hilton Lee's workflow wholesale here. Keep the high-quality master in Sanity with its provenance intact, because provenance in your archive is a feature. When an image is headed for a native social upload, export it, check what it's carrying, and strip the C2PA and XMP blocks locally with a browser-based tool like [removeailabel.com](https://removeailabel.com), so the file never leaves your machine. Upload the cleaned copy and keep the master. Then write the process down in the Studio where your editors will actually see it, because the person doing the Instagram post is probably not the person who read this blog. Whether provenance labels are good for the ecosystem is a separate argument, and I think they probably are. But an AI badge appearing on a client's brand account because nobody checked the file first isn't part of that argument, that's just a missing checklist step. > **Sanity development**: We build Sanity studios and the pipelines around them, image generation included. Sanity Pioneer, first-cohort Ambassador, and about a decade of scar tissue. [See how we work with Sanity](/services/sanity) ## When to render and when to pre-generate The route above renders at request time, and for link unfurls I think that's the right default, because crawlers hit URLs you can't predict, the immutable cache means each image only renders once, and there's no publish-time step to forget. For images humans re-upload, so the Instagram export, the newsletter header, the scheduling tool asset, we pre-generate at publish time and store the files instead. This site's pipeline writes three variants to storage for every post the moment it's created, and the post just references them as plain URLs. A stored file has a stable URL, survives a framework migration, and goes through the metadata-stripping step above exactly once instead of on every download. Screenshotting your own pages with a headless browser is the third option, and I wouldn't bother, because a template only changes when you change it, whereas a screenshot redraws your social cards every time anyone touches the page it points at. The multi-ratio pattern lives in this post rather than the template for now, and if enough people lift it we'll PR it into [turbo-start-sanity](https://github.com/robotostudio/turbo-start-sanity) properly. If you're starting from zero, the 2023 post covers the [single-ratio setup](/blog/dynamic-open-graph-with-sanity-and-next-js), and the template ships the working route today. --- ## Build it right once > The AI shift didn't replace SEO. It raised the bar. Here's what moves the needle across search, AI answers, speed, and accessibility, and what's noise. **TL;DR:** Zero-click search hit 68% of US Google queries. The crawlers behind ChatGPT and Claude still don't run JavaScript. Put those together and one pre-rendered foundation satisfies search, AI answers, speed, and accessibility law at the same time. Here's the evidence for what holds up under testing, and the receipts on what doesn't. **Published:** 2026-07-16 | **Updated:** 2026-08-03 | **Categories:** Hot Takes, AI, Performance, Next.js --- Sanity and Next.js are the stack we build on and sell, so read our enthusiasm for them accordingly. Every figure below is attributed, and nearly all of them link straight to the source. Vendor-funded studies get flagged where they're cited. Where the evidence is contested, we've said so instead of picking the flattering number. Go and check the work yourself; we would. ## The ground moved For years, being found online meant one thing: rank on Google, get the click. That deal is coming apart. In the first four months of 2026, **68% of US Google searches ended without a click** to any website ([SparkToro](https://sparktoro.com/blog/in-2026-less-than-one-third-of-google-searches-still-send-a-click/)). Pew tracked 900 US adults' Google searches through March 2025. When the results page showed an AI summary, people clicked through to a website 8% of the time. When it didn't, they clicked 15% of the time ([Pew](https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/)). That's the AI summary roughly halving the chance a search sends anyone to a website. The comparison isn't clean, since the two sets of searches aren't like for like, and Google disputes the read. So take it as the direction of the drop, not a measured cause. Some publishers went to court anyway. [Penske Media](https://techcrunch.com/2025/09/14/rolling-stone-owner-penske-media-sues-google-over-ai-summaries/) and [Chegg](https://www.cnbc.com/2025/02/24/chegg-sues-google-for-hurting-traffic-as-it-considers-alternatives.html) each filed antitrust suits against Google in 2025, both pointing to falling search referrals among their claims. At the same time, a new channel showed up. People now click through from ChatGPT, Perplexity, and Claude. It's real and it's growing. It's also small: one peer-reviewed study of 973 stores put ChatGPT referrals at under 0.2% of visits, and whether that traffic converts well is a live fight between credible people on both sides ([Marketing Science](https://pubsonline.informs.org/doi/10.1287/mksc.2025.0489)). We'll let them get on with it. So that's the state of play. Classic search still sends most of the traffic, but it's shrinking and getting harder to win. AI answers are a promising new channel that's still unproven. And underneath both sits the thing that makes either one work: a fast, crawlable, well-built site. That part hasn't changed at all. It just has more judges now. The reflex is to treat that as a brand-new problem with a brand-new product bolted on. Buy an AEO tool. Generate an llms.txt file. Sprinkle on some schema. Call it a strategy. We think that's backwards, and we can show you why. ## One foundation, four judges Google's crawler, ChatGPT's crawler, a screen reader, a person on a slow phone: every audience that sizes up your site ends up reading the same thing, your page's structure once it has rendered. What separates them is how much work each one will do to get there. Google's crawler runs your JavaScript, slowly and unpredictably. A screen reader builds its picture from your HTML and your headings. A human just wants the page to load fast and not jump around while they're trying to tap a button. And the crawlers behind ChatGPT, Claude, and Perplexity don't execute JavaScript at all. Vercel's crawler study, run over a single month in late 2024, logged 569 million GPTBot fetches and 370 million from Claude's crawler, and found neither executed any JavaScript ([Vercel](https://vercel.com/blog/the-rise-of-the-ai-crawler)). The volumes have grown a lot since. The finding hasn't moved: as of mid-2026 no ChatGPT, Claude or Perplexity crawler renders JavaScript. If the main content on your page only appears after JavaScript runs, those bots don't see it late. They don't see it at all. One fair exception, since we'd rather you hear it from us: Googlebot feeds Google's AI Overviews, and Googlebot does render JavaScript. That particular surface can reach JS-only content, eventually, through the same slow and unpredictable queue as normal Google indexing. It's not a safe thing to lean on, and it doesn't help you with ChatGPT, Claude, or Perplexity at all. You don't have to take Vercel's word for any of this. Here's our own crawl ledger, rebuilt every day from our server logs. Now look at what fixes it: shipping complete HTML in the first response instead of assembling it in the browser. Server-side rendering does that. So does static generation, and so does ISR. The dividing line isn't server versus browser, it's pre-rendered versus client-rendered. Make that one call and your page is visible to AI crawlers, cheaper for humans to load, and legible to screen readers. Nobody ran three projects to get there. Someone picked the right rendering strategy once. Be precise about what it buys you, though, because this is where people oversell it. Pre-rendering makes your content _present_. It takes semantic HTML and real labels to make it accessible, and a lean bundle to make it fast. Being readable makes you eligible for citations; it doesn't earn them. Build the foundation properly (pre-rendered, semantic, structured, accessible, fast) and it pays off in search, AI answers, speed scores, and accessibility law all at once. **Build it right once.** > **Find out what your pages ship before the JavaScript runs**: Pre-rendering is a routing decision, and it's the one every judge downstream inherits. We make that call per route on Next.js builds, because it's the cheapest thing to get right and the most expensive thing to defer. [See our Next.js service](/services/nextjs) ## What holds up and what doesn't Because the foundation is shared, most of the "do this for AI" checklist turns out to be stuff you should be doing anyway. But plenty of the popular advice is simply wrong, and we'd rather tell you than sell you. ### Holds up under testing - **Pre-rendered HTML**, for the reason above. It's the cheapest thing on this list, because you decide it once when you pick a rendering strategy and every judge downstream inherits the answer. - **Real speed**, measured on real users, not a lab score. It's the one signal all four judges score directly. A clean lab result can hide a bad real one, which is the whole reason field data exists. - **Putting your answer first.** In a hand-mapped sample of 100 AI Overviews, CXL found 55% of citations came from the top 30% of the page ([CXL](https://cxl.com/blog/google-ai-overview-citation-sources/)). Small study, but bigger citation analyses point the same way. - **Concrete evidence in your writing.** In the 2023 GEO paper (peer reviewed at KDD 2024), adding quotations lifted visibility about 41%, statistics about 33%, and cited sources about 28%, while keyword stuffing made things slightly worse ([Aggarwal et al.](https://arxiv.org/abs/2311.09735)). Worth knowing those tests ran against GPT-3.5, so the exact percentages are dated. - **Genuine accessibility**, which is now a legal requirement in the EU, not a nice-to-have. The markup that makes a page work for a screen reader is the same markup a crawler reads, so the work lands twice. The law is below. Here's what the GEO paper measured: | label | Add quotations | Add statistics | Cite sources | Keyword stuffing | | --- | --- | --- | --- | --- | | Change vs baseline | 41 | 33 | 28 | -9 | *What the GEO paper actually measured. Change in visibility against the unmodified baseline, by technique. Controlled benchmark against GPT-3.5, 2023.* The same numbers as a table, since assistants read those better than charts: | Technique | Change in AI visibility vs baseline | | --- | --- | | Add quotations | +41% | | Add statistics | +33% | | Cite sources | +28% | | Keyword stuffing | -9% | The row pointing the wrong way is the one to remember. Stuffing keywords left the page worse off than leaving it alone. ### Doesn't hold up - **Schema markup as a way to push already-cited pages higher.** Ahrefs tracked 1,885 pages that added JSON-LD against 4,000 matched controls, all of them already earning 100+ AI Overview citations, and found no uplift plus a small drop in AI Overviews ([Ahrefs](https://ahrefs.com/blog/schema-ai-citations/)). They're careful to say schema may still help pages AI systems haven't found yet, so this isn't an argument for ripping it out. It's still doing its job for ordinary Google rich results. - **llms.txt as a growth lever.** The file itself is a cheap courtesy index that real agents read when they're handed your domain, so serve one, because it costs you one route handler. What it doesn't do is drive discovery, ranking, or citations, and Google Search has said it won't use the file ([Search Engine Land](https://searchengineland.com/google-says-normal-seo-works-for-ranking-in-ai-overviews-and-llms-txt-wont-be-used-459422)). Expect nothing from it. - **Serving Markdown to bots as a citation play.** It saves real bandwidth, and a randomized A/B test across 381 pages found no statistically significant lift in crawler traffic ([Profound](https://www.tryprofound.com/blog/does-markdown-increase-ai-bot-traffic), an AEO vendor, testing on its own analytics product and publishing a result that cuts against its own pitch). Three weeks rules out a big effect, not any effect, and nobody has shown Markdown earns you citations either way. The boring, durable work pays off. The clever new tricks mostly don't. > **The same ledger, run against your site**: We start with what your pages already serve each crawler, and which line items on your AEO list you can stop paying for. Expect some of it to come back as a recommendation to spend nothing. [See our GEO service](/services/geo) ## One model, many readers We just said serving Markdown to bots won't buy you citations. So why does every block in our Sanity starter ship a Markdown serializer? Because two different things go by that name, and only one of them is a trick. The trick is generating a Markdown export and hoping the answer engines reward you for it (see above: they don't). The other version is plumbing, and it's easier to show than describe. Everything below is real code from [our Sanity starter](https://github.com/robotostudio/turbo-start-sanity), pinned to the commit we're writing against, so you can open any of it and check us. It starts with where the code lives. One block, one folder, and every output it owes anyone: ```text faq-accordion/ ├── faq-accordion.schema.ts # the shape, defined once in Sanity ├── faq-accordion.groq.ts # the query that fetches it ├── index.tsx # HTML, for the person ├── json-ld.ts # FAQPage schema, for Google ├── markdown.ts # structured Markdown, for the agent ├── faq-accordion.test.tsx ├── json-ld.test.tsx ├── faq-accordion-markdown.test.tsx └── thumbnail.png ``` That's the whole folder, all nine files. You define the shape once and query it once. Everything else is a projection of it, and each projection has a test holding it honest. Here's the Markdown one, with its import block cut and nothing else: ```ts export function faqAccordionToMarkdown( block: MarkdownBlock, options: MarkdownOptions ): string { const faqs = (block.faqs ?? []) .filter((faq) => faq?.title) .map((faq) => joinSections([ headingToMarkdown(faq.title, 3), portableTextToMarkdown(faq.richText, options), ]) ); const link = block.link; const linkLabel = (link?.description || link?.title || "").trim(); const linkMarkdown = linkLabel ? mdLink(linkLabel, link?.href) : ""; const subtitle = (block.subtitle ?? "").trim(); return joinSections([ eyebrowToMarkdown(block.eyebrow), headingToMarkdown(block.title, 2), subtitle ? escapeMarkdown(subtitle) : "", ...faqs, linkMarkdown, ]); } ``` [Full source](https://github.com/robotostudio/turbo-start-sanity/blob/ba9ef629039a7b5d313a01a3c10b8749729ff848/packages/sanity-blocks/src/faq-accordion/markdown.ts), if you'd rather read it in place. The block's title becomes an h2. Each question becomes an h3. Each answer runs through the Portable Text serializer, which is what keeps the bullets and the links alive. The eyebrow, the subtitle and the trailing link come along too, because they're fields on the block and an agent should get the same ones a person gets. Nothing scrapes the rendered page, and nothing gets flattened. So an agent asking for that block gets this: ```markdown ## Frequently asked questions ### Do you support multiple content types? Yes, pages, posts, and product blocks all serialize through the same pipeline. ### How is this exposed to AI agents? Every block ships three projections automatically: - HTML for the rendered page - JSON-LD for search engines - Markdown for agents Full spec: [schema.org/FAQPage](https://schema.org/FAQPage) ``` Every question is a real heading. Every answer keeps its list and its link. An agent reading that knows which answer belongs to which question, because the structure never left. Skip the serializer and here's what you hand over instead: ```json {"title":"Frequently asked questions","faqs":[{"title":"Do you support multiple content types?","richText":[{"_type":"block","style":"normal","markDefs":[], "children":[{"_type":"span","text":"Yes, pages, posts, and product blocks all serialize through the sam... ``` That's your CMS's internal plumbing. The meaning is in there somewhere. **You're just making a language model dig for it.** Your content is already structured, or it is if you've modeled it properly (that's what [the Sanity AEO/SEO defaults we ship](/blog/aeo-seo-best-practices-for-sanity) are for), so let each reader take the shape it can parse. A whole page is the same idea one level up. Walk the blocks and ask each one: ```ts function blockToMarkdown( block: MarkdownBlock, options: MarkdownOptions ): string { switch (block?._type) { case "hero": return heroToMarkdown(block, options); case "richTextBlock": return richTextBlockToMarkdown(block, options); case "faqAccordion": return faqAccordionToMarkdown(block, options); // four more cases, one per block type default: return ""; } } export function pageBuilderToMarkdown( blocks: MarkdownBlock[] | null | undefined, options: MarkdownOptions = {} ): string { if (!Array.isArray(blocks)) { return ""; } return blocks .map((block) => blockToMarkdown(block, options)) .filter((markdown) => markdown.trim()) .join("\n\n"); } ``` Four of the seven `case` arms are cut there, along with the imports. [Full source](https://github.com/robotostudio/turbo-start-sanity/blob/ba9ef629039a7b5d313a01a3c10b8749729ff848/packages/sanity-blocks/src/internal/page-builder-to-markdown.ts). Look at the `default`. An unknown block type returns an empty string, so a new block is invisible to agents until someone writes its `markdown.ts`. That's a deliberate cost, not an oversight: the alternative is guessing at a shape nobody defined, and a bad guess is worse than a gap. Then serve it from the address the humans already use: ```ts /** * Content negotiation for Markdown: a `.md` URL or `Accept: text/markdown` is * rewritten to the `/api/markdown` route handler; everything else passes through. * The Markdown route sets `Vary: Accept` so a shared cache never serves Markdown * to a browser; App Router HTML pages can't carry it (Next owns that header), so * the `.md` suffix is the cache-safe surface. */ export function proxy(request: NextRequest): NextResponse { if (request.method ! "GET" && request.method ! "HEAD") { return NextResponse.next(); } const { pathname } = request.nextUrl; const hasMdSuffix = pathname.endsWith(".md"); const wantsMarkdown = hasMdSuffix || prefersMarkdown(request.headers.get("accept") ?? ""); if (!wantsMarkdown) { return NextResponse.next(); } const rawPath = hasMdSuffix ? pathname.slice(0, -3) : pathname; const contentPath = normalizeMarkdownPath(rawPath); const url = request.nextUrl.clone(); url.pathname = "/api/markdown"; url.search = ""; url.searchParams.set("path", contentPath); return NextResponse.rewrite(url); } export const config = { matcher: ["/((?!api/|_next/|favicon.ico|robots.txt|sitemap.xml|llms\\.txt).*)"], }; ``` The docblock there is verbatim and it's about to do some work. Cut from the body: the guard that skips asset files when negotiating by header, and the request header that carries the path through the rewrite. It's called `proxy.ts` because Next.js 16 renamed `middleware.ts`; on 15 and earlier this is your middleware. [Full source](https://github.com/robotostudio/turbo-start-sanity/blob/ba9ef629039a7b5d313a01a3c10b8749729ff848/apps/web/src/proxy.ts). That's content negotiation. It's about as old as HTTP, and you're pointing it at a new kind of reader. [The full Next.js implementation](/blog/nextjs-aeo-geo) is a couple of rewrites and a route handler. Do it for bandwidth and fidelity, not for a ranking bump that isn't coming. ### Why there's a .md suffix at all Now read that docblock again, because it's the part we'd most want challenged. We key off the `Accept` header and a `.md` suffix, not a list of bot user-agents, because that list is stale the week you write it. The header is the elegant half, and on its own it's what [our Next.js guide](/blog/nextjs-aeo-geo) tells you to use. The suffix is there for a duller reason. In the App Router, Next owns the `Vary` header on an HTML response, so the page can't advertise that it varies on `Accept`. Put a shared cache in front of that and it can hand a browser the Markdown it cached for an agent. The route handler can set `Vary: Accept` because we own that response. The page can't. So the suffix is the cache-safe surface. The Markdown twin ships `x-robots-tag: noindex, nofollow` plus a `content-location` header pointing back at the canonical page, which is what keeps it out of search ([route handler](https://github.com/robotostudio/turbo-start-sanity/blob/ba9ef629039a7b5d313a01a3c10b8749729ff848/apps/web/src/app/api/markdown/route.ts)). The cost is real, so here it is up front. Bing's Fabrice Canel made the case against in one line: "really want to double crawl load? We'll crawl anyway to check similarity." Every `.md` URL is a second URL per page, our own guide tells you not to advertise them, and the starter's llms.txt lists them anyway. Yes, those two things contradict each other. We picked cache correctness over crawl budget. If you're running a thousand pages and Bing is the crawler you care about, pick the other way. None of this is an AI strategy, and we won't dress it up as one. One content model, three projections: HTML for the person, JSON-LD for Google, Markdown for the agent. The fields are the same ones every time; nothing gets scraped from anything else. It's the foundation from earlier, emitting one more honest view of itself. > **The projections are only as good as the model underneath**: We build Sanity content models where every block owns its own HTML, JSON-LD, and Markdown, with a test on each projection. That's the folder above, and it's open source if you'd rather just take it. [See our Sanity service](/services/sanity) ## What this looks like in practice Take brightonSEO, the conference, which is about as SEO-literate an audience as exists. They moved off WordPress onto Sanity and Next.js. The second site they needed, for their first US conference, went up in two days ([Sanity's own case study](https://www.sanity.io/customers/brighton-seo)). Moving to a modern, future-proof headless solution using Sanity and Next.js means we move much faster with new initiatives. Kelvin credits [headless in general](/services/headless-cms). We'll be more specific: the second site took two days because there was nothing left to build, only content to point at. The first build was pre-rendered and structured from day one, so the foundation was already finished. Two days is how long it takes when the only job left is pointing content at it. And it's the same foundation the crawlers, the screen readers, and the people on slow phones are all reading. ## Migrations are where it breaks We're not going to hand you a tidy success story and stop there. Replatforming carries real risk. Dan Taylor at SALT.agency has been tracking this: his June 2026 study of 1,052 domain migrations found a median recovery time of 304 days for organic traffic, with a mean of 489 dragged up by a long tail. About 14% of sites were still short of their old numbers three years on. The data is crowdsourced from SEO practitioners, not randomly sampled, and it measured domain moves, not CMS replatforms. So it tells you how bad a botched move can get, not what your own odds are. We once inherited a site that had lost more than half its traffic after a headless migration. The cause was a single robots.txt rule carried over from the old stack, blocking the very scripts the new pages needed to render. Every page was technically live and effectively blank. None of that is a reason to avoid replatforming. It's a reason to do it deliberately, with someone who has mapped every redirect, audited the crawl rules, and modeled the content before cutover, not after. Ours is written down as a [pre-launch migration checklist](/blog/the-pre-launch-essentials-checklist-for-cms-migrations), and it exists because the failure above is boring and preventable. The gap between the 272% ROI case ([Forrester, commissioned by Sanity](https://tei.forrester.com/go/Sanity/Sanity/?lang=en-us)) and the traffic-halving disaster is almost entirely execution. > **Replatforming without the 304-day version**: Redirects mapped, crawl rules audited, content modelled before cutover. It's the checklist linked above, run by the people who wrote it. [See our migration service](/services/cms-migration) ## Accessibility stopped being optional One more shift is worth naming, because it's now law. The European Accessibility Act took effect on 28 June 2025 ([EU Commission](https://accessible-eu-centre.ec.europa.eu/content-corner/news/eaa-comes-effect-june-2025-are-you-ready-2025-01-31_en)). If you put products or services on the EU market, accessibility is a legal requirement, and it keys off where you sell, not where you're incorporated. The carve-out is narrower than it looks, and this is the part most write-ups get wrong. A microenterprise means under 10 staff and under €2M turnover or balance sheet, and even then only your **services** are exempt. A microenterprise that makes or sells a covered product still complies. Each country sets its own penalties and the range is enormous. Spain's very serious infringements run up to €1M. For the rest of the EU, the numbers you'll find online contradict each other, so we won't quote a figure we haven't checked against the actual law. This is general information, not legal advice, so check your actual obligations for the markets you sell into. A warning that catches people out: an accessibility overlay widget isn't a reliable substitute for accessible code. In 2025, 24.9% of US accessibility lawsuits named sites that already had a widget installed ([EcomBack's lawsuit tracker](https://www.ecomback.com/annual-2025-ada-website-accessibility-lawsuit-report)). That suggests an overlay on its own hasn't reliably headed off claims. A script bolted on at runtime can't invent a heading order that was never authored, and it can't label a control your build left unlabeled. The good news is the convergence, again. The same semantic HTML a screen reader depends on is the structure an AI crawler reads and Google rewards. Do it properly and you're covered on three fronts with one piece of work. ## Knowing whether you're winning Judge your speed on real-user field data (Google's CrUX, Search Console), not a one-off Lighthouse run. CrUX grades you at the 75th percentile, so by definition a quarter of your visits are worse than that number, and a single lab run doesn't capture that spread at all ([web.dev](https://web.dev/articles/lab-and-field-data-differences)). Your AI traffic won't show up cleanly in analytics yet. Build a custom channel for the AI referrers ([our PostHog setup](/blog/we-dumped-google-analytics-for-posthog) is where ours lives), and remember your server logs are the only place those crawlers actually appear. That's the data behind the scoreboard further up this page, and it costs nothing to start collecting today. Watch the order it arrives in, too. Crawling comes first, citations second, clicks last. A rise in AI crawler hits with no referrals behind it yet is what you should expect, not a failed experiment. Reading it as failure is how teams talk themselves out of the work a year before it pays. ## How we do this Everything above is the order we spend a client's budget in. Rendering strategy first, because every judge downstream inherits that decision and it's the one that hurts to revisit. Content model second, because the projections are only as good as the fields behind them. Semantics and speed third, which is the work that turns up in a field report and in a legal letter. AEO line items last, and some of them we talk clients out of. Two builds where that order held. We picked up Mario Testino's site in a state of crisis: a botched migration had left 160+ redirects missing and Sanity API overages running at 200%. The fix was unglamorous, back to vanilla Sanity, rebuild the model, recover every redirect, clean up the navigation so crawlers could read it. Tray.ai was the rendering call at scale: 500,000 pages onto Vercel in six weeks, hybrid static and server rendering across 25 page types, builds down from a full workday to two minutes. > **Want the foundation built properly?**: We build pre-rendered, structured, accessible sites on Next.js and Sanity, and we'll show you our own crawl data before you sign anything. [See our GEO service](/services/geo) ## Where this leaves you There's no silver bullet here, and anyone selling you one is worth ignoring. The fundamentals became the strategy while everyone was out shopping for tools. A site that's fast, pre-rendered, structured, and accessible competes in Google, shows up in AI answers, and leaves you on far firmer ground if you're challenged, because all of those judges are reading the same thing. If you want a shopping list, it's short and it's unfashionable. Fund the rendering strategy, the content model, the semantics, and the speed. Fund the migration properly if you're doing one. Then spend what's left on writing things worth quoting. That's the half no vendor can sell you, and it's where the evidence keeps pointing. The scoreboard further up this page rebuilds from our server logs every day, so when the picture changes, we'll update the post to match. Until then, the work is the list above. --- ## Next.js 16.3 for dummies > Instant navigations explained in plain English, plus our upgrade to 16.3, the cache components PR we parked, and the 341KB we cut instead. **TL;DR:** Next.js 16.3 (stable since August 3, 2026) brings instant navigations (SPA-feel link clicks on a server-driven app), plus huge Turbopack memory cuts, native Node.js streams rendering, agent-first tooling, and a deprecated edge runtime. We upgraded this site to the preview in an afternoon, built the full instant-navigations adoption on a branch, then parked it because full SSG already navigates instantly. The thing that made our site feel faster was cutting 341KB of gzipped JavaScript with dynamic imports. An edit note covers the preview-to-stable diff and our build benchmarks. **Published:** 2026-07-16 | **Updated:** 2026-08-11 | **Categories:** Next.js, Performance, Vercel --- We've covered [Next.js 16](/blog/nextjs-16-for-dummies), [16.1](/blog/nextjs-16-1-for-dummies), and [16.2](/blog/nextjs-16-2-for-dummies) in this series, and now Vercel has started publishing what's coming in 16.3. The lead feature is instant navigations: clicking a link on a server-rendered site responds immediately, the way it does in a single-page app. **16.3 went stable on [August 3](https://nextjs.org/blog/next-16-3).** We wrote this post against `16.3.0-preview.5` and it stands as written below; this note covers what changed between the preview and stable, and what we measured after bumping this site to `16.3.0`. **New since the preview:** - **The build cache is on by default.** The persistent Turbopack cache for `next build` graduated from `experimental.turbopackFileSystemCacheForBuild` to a default. Vercel reports up to 5.5x faster CI builds; our numbers are below. - **Prefetch inlining.** Small prefetch payloads now get bundled together automatically. Apps upgrading from 16.2 saw [45% fewer prefetch requests on average](https://vercel.com/blog/vercel-supports-next-js-16-3), some over 70%. - **Immutable static assets.** Hashed assets can be reused across deployments: 17% fewer CDN requests and 24% fewer bytes transferred for static content in Vercel's fleet numbers. - **TypeScript 7 type checking.** The "faster TypeScript, experimentally" line from the grab bag shipped for real: TS7 is out, and `next build` uses it for type checking once you bump your local `typescript` dependency to `^7`. - **ISR learned the shell trick.** Pages you don't prerender with `generateStaticParams` now serve an instant loading shell to their first visitor, then upgrade to the prerendered page in the background instead of blocking. - **Offline resilience (experimental).** With `experimental.useOffline`, navigations and Server Actions that would throw on a dropped connection stay pending and retry on reconnect, plus a `useOffline` hook for showing a banner. - **Stabilized names.** The `instant()` Playwright helper, `catchError`, `retry`, and `export const prefetch` all dropped their unstable prefixes for good. - **One correction to the post below:** Vercel retired the first-party docs skills. The auto-managed `AGENTS.md` block does that job now, so don't go looking for them. - **Sharp edges filed down.** The styled-jsx style leak from our sharp-edges list is fixed in stable, and the vendored lodash got a CVE patch. **Our upgrade, preview.5 to stable:** the bump itself was one version change, but the monorepo bit us again, in a new place. Next.js 16.3 now includes `@types/node` in its peer fingerprint, and two of our apps pinned `^20` while the rest sat on `^24`. That split `next@16.3.0` into two type-graph instances, and `next.config.ts` exploded with the same baffling two-copies errors as last time. The fix was aligning `@types/node` across the workspace, not an override. If your typecheck breaks after the stable bump, check for a duplicate `next` first; it's always a duplicate `next`. **Did stable get faster?** We benchmarked `next build` on this site (Apple Silicon, ~70 static routes, same machine, same content) before and after: | Build | `16.3.0-preview.5` | `16.3.0` stable | | --- | --- | --- | | Cold (no cache) | 50.2s total / 9.6s compile | 50.4s total / 8.9s compile | | Repeat build | ~50s (no build cache by default) | **23.6s total / 1.6s compile** | | build | preview.5 (s) | 16.3.0 stable (s) | | --- | --- | --- | | Cold build | 50.2 | 50.4 | | Repeat build | 50.2 | 23.6 | *next build on this site, preview.5 vs 16.3.0 stable (seconds). Our measurements: apps/web, same machine, cold = .next wiped, repeat = second consecutive build* Cold builds are flat, which is what we expected: our build time is mostly static generation and type checking, not compilation. The win is the now-default build cache, which took one settling build to warm up and then cut repeat compiles from 8.9s to 1.6s and total build time roughly in half. On CI that carries `.next/cache` between runs, that's the difference you'll actually feel. Runtime-wise there's nothing to measure on a fully static site; the 22% more requests under load from Node.js streams applies to apps that render per-request, not ours. The verdict from the post below hasn't moved: `cacheComponents` and `partialPrefetching` are still opt-in, our adoption PR is still parked, and this site is still plain static files, just built faster. > **This post was written against the preview.** Everything below comes from running `16.3.0-preview.5` in production on this site (preview.6 landed July 13), the three official posts ([instant navigations](https://nextjs.org/blog/next-16-3-instant-navigations), [AI improvements](https://nextjs.org/blog/next-16-3-ai-improvements), [Turbopack](https://nextjs.org/blog/next-16-3-turbopack)), and a dig through the preview release notes on GitHub. The edit note above covers what changed by the time 16.3 went stable; the original text stands as written. We upgraded this site to the preview the week it dropped, built the full instant-navigations adoption on a branch, and merged none of it, because a fully static site already navigates instantly. The change that made this site feel faster was cutting **341KB** of gzipped JavaScript. ## What's new and shiny The 16.3 line runs to six preview tags and 87 canaries as of mid July. We've put the biggest feature first. ### Instant navigations: stream, cache, or block Click a link on a typical server-rendered site and three things happen: you click, the browser waits for the server, and the next page appears. The wait in the middle can be 100ms or a full second depending on latency and how much work the server does, and while it lasts the page doesn't move. That gap is why apps like Linear went client-heavy in the first place: an SPA puts *something* on screen immediately, then fills in the data. 16.3 keeps the server-driven model and goes after the wait. With the new flags on, every route has to answer one question: what can the user see immediately when they navigate here? Three answers count. **Stream.** Wrap slow data in ``. The user gets the page shell with loading states straight away, and content streams in as the server finishes. This is the SPA feel. **Cache.** Mark data fetches with `'use cache'`. The user gets previously cached UI, reused across requests, with no loading state at all. **Block.** Some pages should wait. A blog post that flashes a skeleton for half a second is worse than a blank beat followed by the full article. Those routes opt out explicitly: ```tsx title="page.tsx" export const instant = false; ``` The enforcement is the clever bit. In development, a navigation that can't respond instantly is now an **error**, surfaced in a new Instant Insights panel. You fix it by streaming or caching, or you declare the blocking deliberate. Slow navigations stop being something users find in production and become something the framework flags at dev time. There's a Playwright helper to hold that line in CI: ```tsx import { instant } from '@next/playwright'; await instant(page, async () => { await page.click('a[href="/products/hats"]'); await expect(page.locator('h1')).toContainText('Baseball Cap'); }); ``` Assertions inside the `instant()` block have to pass without any network roundtrip. Existing tests check whether a page works; this checks whether it responds before the server does. ### Partial prefetching: one shell per route Until now, Next.js fired a prefetch request for every link in the viewport. A sidebar with twenty chat links meant twenty prefetch requests, all pointing at the same route. Vercel's own words: "Many of you told us that this looked ridiculous, and frankly, we agree." 16.3 prefetches one reusable shell per route instead: one for `/chat/[id]`, one for `/dashboard`, cached for the whole session. Click any of the twenty chat links and the shell renders immediately while that chat's data streams in. If a shell isn't enough (say you want the chat header to arrive with real content), you escalate per link: ```tsx ``` Even then, Next.js only prefetches down to what's synchronously available or marked `'use cache'`. Prefetching used to be all-or-nothing, which was the main reason to turn it off entirely; now there's a middle setting. Both behaviors sit behind flags in `next.config.ts`, and both are planned defaults in a future major: ```typescript title="next.config.ts" const nextConfig: NextConfig = { cacheComponents: true, partialPrefetching: true, }; ``` The DevTools also grew a Navigation Inspector that pauses every navigation at the shell stage. It's the fastest way we found to see exactly what a user gets during that first paint. ### Turbopack stops holding everything in RAM Dev memory eviction is now on by default: Turbopack moves cold compiler state out to the filesystem cache instead of holding everything in RAM. Vercel's own dashboard app dropped from 21.5GB to 2GB of dev-server memory, nextjs.org went from 4.6GB to 0.84GB, and a user in the [feedback thread](https://github.com/vercel/next.js/discussions/95130) reported 20GB down to 5GB. | app | Before (GB) | With eviction (GB) | | --- | --- | --- | | vercel.com | 21.5 | 2 | | nextjs.org | 4.6 | 0.84 | | Community report | 20 | 5 | *Turbopack dev-server memory, before and after eviction (GB). Source: Vercel's Turbopack post and discussion #95130* Builds get the same treatment behind `experimental.turbopackFileSystemCacheForBuild`: the persistent cache that already speeds up dev now applies to `next build`, so CI can carry `.next` between runs. Vercel's numbers again: | build | Cold (s) | Warm cache (s) | | --- | --- | --- | | nextjs.org | 21 | 9.2 | | vercel.com/home | 66 | 46 | | vercel.com/geist | 30 | 5.5 | *next build times with the filesystem cache (seconds). Source: Vercel's Turbopack post* Two smaller Turbopack items: an experimental Rust port of the React Compiler (`experimental.turbopackRustReactCompiler`, 20-50% faster compilation in their tests, Turbopack-only), and Vite-compatible `import.meta.glob` support, which matters if you're migrating from Vite or you load content files by pattern. Every benchmark above is Vercel measuring Vercel's own apps, plus one community report. Nobody independent has reproduced them yet, which is normal three weeks into a preview, but treat them as directional. ### The framework now assumes an agent is in the loop 16.3 is the first release where the framework openly assumes an AI agent is part of the dev loop. `next dev` detects a coding agent and writes a managed block into your `AGENTS.md`, pointing the agent at version-matched docs bundled inside `node_modules/next/dist/docs`. The block keeps itself current across upgrades, and `agentRules: false` in the config turns it off. The docs went agent-readable too: append `.md` to any nextjs.org/docs URL and you get plain markdown. We shipped [the same markdown-twin pattern on this site](/blog) last year. The repo now ships four first-party agent skills: one that adopts cache components for you, one that optimizes routes after adoption, one that adopts partial prefetching, and a dev-loop skill that connects your agent to the running dev server through a new `/_next/mcp` endpoint. That MCP server has a `compile_route` tool, so an agent can ask "does this route compile" in seconds instead of paying for a full `next build` to find out. Dev errors got the same treatment. Every Instant Insights error carries a Copy prompt button that produces a ready-to-paste agent prompt, and the error docs pages are structured (patterns, trade-offs, gotchas) so an agent can act on them without a human translating. Having built our whole content pipeline around agents, we rate this as the sleeper feature of the release. ### Node streams by default, edge runtime deprecated The "better rendering under stress" tease from the announcement doesn't have its own post yet, but the release notes show what it is: rendering moved off Web Streams onto native Node.js streams, on by default since canary 38. If your app serves heavy server-rendered pages under load, this is where that improvement lives. Also in the release notes: **the edge runtime is deprecated** as of this line. We hit it from the other side, since adopting cache components on our branch forced our OG image route off the edge runtime and onto Node anyway. If you export `runtime = 'edge'` anywhere, start planning the move now rather than at the next major. ### Security patches now arrive monthly Next.js security fixes used to ship whenever a fix was ready, which meant keeping up was a matter of watching the releases page. As of July 13 that's a schedule: one security release a month, with the first due July 20. That first release patches 16.2 and 15.5. Two useful facts fall out of that. Stable 16.3 is still at least a few weeks away, and the 15.x line is still getting security fixes, so teams on the older major can upgrade on their own timetable. If you maintain a Next.js site, a monthly bump alongside the release cadence covers you, and it's a five-minute job now that the timing is predictable. No help for us, mind. This site runs the 16.3 preview in production, which is exactly the sort of thing a security schedule exists to discourage, and we do it so we can write posts like this one. Every client project sits on patched stable, upgraded monthly, boring on purpose. Our own site takes the preview tags and finds out. If something in 16.3 was going to break a production deploy, we'd much rather it broke ours. ### The grab bag, explained simply The rest of the release notes are smaller features whose one-line changelog entries assume you already know what they mean, so here's the plain-English version of each. **Service workers, compiled properly.** A service worker is a small script the browser keeps around for your site even when no tab is open. It's what makes offline mode, push notifications, and "install this site as an app" work. Next.js never had first-class support, so PWA setups leaned on community plugins and files copied into `public/`. Turbopack now compiles service workers like any other entry point and serves them from `/_next/static`, in both App and Pages router. **`next/root-params`.** If your app starts with a dynamic segment like `app/[lang]/`, every component that needed the language took it as a prop, threaded down layer by layer. Now it's `import { lang } from 'next/root-params'` and `await lang()` from any server component, no prop threading. The getters come from your actual folder names, so the API surface is literally your directory structure. New in 16.3, server components only for now. **`'use cache: private'`.** Regular `'use cache'` output is shared between all visitors, which is exactly wrong for anything derived from cookies (a username in the header, a cart count). The `private` variant caches in that one visitor's browser instead, so personalized fragments can still take part in instant navigations without one user seeing another's data. **`staleTimes`.** The router keeps in-memory copies of pages you've visited so going back is instant. `staleTimes` is the dial for how long those copies are trusted before refetching: `static` for fully prefetched pages (default 5 minutes) and `dynamic` for everything else (default 0, meaning always refetch). Still experimental, and it answers "why does clicking back sometimes refetch and sometimes not". **Graph CSS chunking.** Bundlers have to decide how to split your CSS into files. Split too little and every page downloads all your styles; split per route and shared styles get duplicated or arrive in the wrong order, which is how you get the classic "this page looks different when I navigate to it versus when I refresh on it" bug. The new `experimental.cssChunking: "graph"` mode groups styles by which ones are actually used together across pages, instead of guessing from the route tree. **`catchError` and `retry` lost their prefixes.** We flagged both in [the 16.2 post](/blog/nextjs-16-2-for-dummies) as `unstable_` experiments you shouldn't build on. In 16.3 the prefixes are gone: `catchError` handles server component errors without needing a whole `error.tsx` boundary, and `retry` inside an error boundary re-fetches the data rather than just clearing the error state and hoping. **A faster TypeScript, experimentally.** Microsoft is rewriting the TypeScript compiler in Go (the project is called tsgo, and it typechecks roughly 10x faster). 16.3 adds an experimental flag to use it as the CLI's TypeScript backend. If your `next build` spends most of its time in typechecking, this is the line to watch. ### Feature status at a glance | What | Status in the 16.3 preview | | --- | --- | | Instant navigations (stream / cache / block) | Opt-in via `cacheComponents: true` | | Partial prefetching (one shell per route) | Opt-in via `partialPrefetching: true` | | `instant()` Playwright test helper | New `@next/playwright` package | | Turbopack dev memory eviction | On by default | | Persistent cache for `next build` | `experimental.turbopackFileSystemCacheForBuild` | | Rust React Compiler | Experimental, Turbopack-only | | Native Node.js streams rendering | On by default | | `AGENTS.md` auto-managed block | On when a coding agent is detected | | Docs as markdown (`.md` suffix, llms.txt) | Live on nextjs.org | | Monthly security releases | First one July 20, patching 16.2 and 15.5 | | Edge runtime | Deprecated | ## Our upgrade experience We bumped this site (a pnpm monorepo with three Next.js apps) to `16.3.0-preview.5` on June 30. The upgrade itself was uneventful: React 19.2.3 already satisfied the peer range, no code changes, production build green on the first try. The one gotcha was monorepo-specific. A transitive peer dependency (a workflow package that peers on `next >13`) resolved its own copy of `next@16.2.1` alongside our preview version. Two copies of Next.js in one type graph means `check-types` explodes with baffling errors about incompatible internal types. The fix is a version-specific pnpm override in the root `package.json`: ```json title="package.json" { "pnpm": { "overrides": { "next@16.2.1": "16.3.0-preview.5" } } } ``` That collapses everything to one resolution. If you're in a monorepo and your typecheck breaks after the bump, check for a duplicate `next` before you blame the preview. Upgrade, override, green build: done in an afternoon. Two weeks of running the preview in production since, and zero runtime issues. The risk in a preview is the APIs changing under you, not the code falling over, which is why the new flags stay off on this site until stable. ## The PR we didn't merge We did the full adoption on a branch: `cacheComponents` and `partialPrefetching` on site-wide, the disallowed segment configs (`dynamic`, `revalidate`, `dynamicParams`) removed from every page and route handler, blog rendering wrapped in a `'use cache'` boundary, build-time non-determinism fixed (cache components forbids it), the OG image route moved off the edge runtime, plus a 19-test Playwright suite asserting instant navigation. Everything came back green. Then we clicked around both deployments for a week and couldn't feel the difference. The reason is boring: this site is already fully static. Every content page is prerendered at build time and served as files from a CDN. There's no server work for a loading shell to hide, so the navigation delay was never server-side. Cache components would have added a preview flag, a determinism constraint on the whole codebase, and soft-404 shell behavior we had to `noindex` around, in exchange for a difference we could not perceive with our own hands on the trackpad. So the PR sits open, rebased and waiting for stable, and this site is still plain static files. ### Spin up every variant, then click around The method is the takeaway here, more than the verdict. We didn't debate rendering strategy in a doc. We had three deployments up at the same time: 1. Full SSG (main, the control) 2. Cache components with partial prefetching 3. Cache components without partial prefetching Then we navigated all three by hand, over an average office connection. We skipped the synthetic benchmarks because perceived navigation speed barely registers in metrics, and you can feel it within thirty seconds of using the site. This used to be an expensive way to decide, a day or more of engineer time per variant. With coding agents it's close to free: describe the variant, let the agent build the branch, get a preview URL per PR. Two of our three PRs existed for a week purely so we could click around them and close them, and they settled an argument no benchmark could. ## What actually made the site feel faster While we were testing rendering variants we found the real problem, and it wasn't rendering strategy. It was payload. Our MDX component map registered every component as an eager client reference on every content page. A decorative WebGL particle effect on team member cards was shipping `three` and `@react-three/fiber` (roughly 400KB) to every blog post. Four chart components used by exactly one post were shipping `recharts` (about 110KB) to all of them. Syntax highlighting ran client-side too, so `react-refractor` and its language grammars rode along on every page. The fix was dynamic imports with `ssr: false` behind `"use client"` wrappers, which moves the heavy leaves into on-demand chunks outside the page manifest. Syntax highlighting moved to the server entirely, since it renders static markup with no hooks; only a small copy-button island stays client-side. One subtlety worth knowing: `next/dynamic` inside the RSC renderer does not help here, because the client-reference manifest still bundles the component. The dynamic import has to live inside a client component. The measured result on content pages, from the merged PR: | Metric | Before | After | Change | | --- | --- | --- | --- | | Eager first-load (gzip) | ~1196KB | ~855KB | **-341KB (-29%)** | | three + @react-three/fiber | Every content page | On demand | ~400KB deferred | | recharts | Every content page | On demand | ~110KB deferred | | Syntax highlighting | Client bundle | Server-rendered | Grammars never ship | | SSG classification | 51 SSG / 20 static | 51 SSG / 20 static | Unchanged | | state | kb | | --- | --- | | Before | 1196 | | After dynamic imports | 855 | *Content-page eager first-load, gzipped (KB). Source: our merged bundle-trim PR* This is the change we could feel. Less JavaScript means the browser parses, compiles, and hydrates less before the page responds to input. The click-to-movement gap that instant navigations targets got shorter on our site because there was less code standing between the click and the movement. Both fixes are real: instant navigations is the right one for server-time gaps, and a smaller bundle is the right one for client-time gaps. Check which one you actually have before reaching for either. > **Next.js performance work**: Bundle audits, rendering strategy, and Core Web Vitals on real production sites. [See how we work with Next.js](/services/nextjs) ## Should you enable it? Depends what you run. **App-like sites (dashboards, feeds, anything personalized or data-heavy):** yes, this feature is for you. Your navigations do real server work, and a streaming shell is a much better answer than either blocking or client-fetching everything. Try the preview on a branch now, ship it at stable. **Content sites that can go fully static:** you already have instant navigations. They're called static files. Spend the effort on your bundle instead; run a build analysis and find your equivalent of a 400KB particle effect. We'd bet money you have one. **Everything in between:** the flags are opt-in per project but the *discipline* is per route. Enabling them forces every route to declare stream, cache, or block, which is a useful audit even if you end up blocking half your routes. ## Quick comparison | | Next.js 16.2 | Next.js 16.3 (preview) | | --- | --- | --- | | Link prefetching | Every link in the viewport | One reusable shell per route (opt-in) | | Slow navigations | Users find them in production | Dev-time errors in Instant Insights | | Turbopack dev memory | Compiler state held in RAM | Cold state evicted to disk by default | | Persistent Turbopack cache | Dev only | `next build` too (experimental) | | Rendering | Web Streams | Native Node.js streams | | React Compiler | JavaScript implementation | Experimental Rust port (Turbopack-only) | | `catchError` / `retry` | `unstable_` prefixed | Stable | | Agent tooling | `AGENTS.md` by hand | Managed block, four skills, `/_next/mcp` | | Edge runtime | Available | Deprecated | | Security releases | Whenever a fix was ready | Monthly, from July 20 | ## How to upgrade Three routes, depending on how much you want to adopt today. **Option 1: bump the version, leave the flags off.** This is what we run in production: ```bash npm install next@preview ``` For most apps that's the whole job. Ours needed the pnpm override above, and yours might need its equivalent if you're in a monorepo. **Option 2: let an agent do the adoption.** Vercel published an [agent skill for adopting cache components](https://github.com/vercel/next.js/tree/canary/skills/next-cache-components-adoption), and the migration is mostly mechanical: remove segment configs, add cache boundaries, fix determinism. It's a sensible thing to delegate, and it's how we'd redo our branch when stable lands. **Option 3: wait for stable.** There's no date yet, and the July 20 security release targets 16.2 and 15.5, so stable 16.3 is at least a few weeks out. Waiting costs you nothing except time with the Instant Insights panel; the flags will still be opt-in when it lands. If you do flip the flags, expect errors on day one. Each one is a route that's slow today without anyone having noticed, which is exactly what you flipped the flags to find out. The [preview docs for instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) cover the details. ### Current sharp edges The [feedback thread](https://github.com/vercel/next.js/discussions/95130) is the live list of what breaks right now. Worth knowing before you flip the flags: - **Static exports don't work with partial prefetching.** If you ship `output: 'export'`, this whole feature set isn't for you yet. - **Instant Insights false-flags parallel routes** that never actually render, so you may get errors about slots you can't fix. Under review. - **Global styled-jsx styles can leak between routes** when UI state is preserved across a navigation. Scope your overrides. - **`turbopackIgnore` comments don't silence dynamic `path.join()` file access**, which can trace entire directories into your standalone output and balloon deploy size. A fix is in flight. - **Self-hosting on SST** (v4.17+) with `cacheComponents` can break SSR entirely; the current workaround is turning the flag off. Vercel and AWS/OpenNext deploys are reported working. - **Safari's dev tooling is flaky** for Instant Insights; use Chrome or Firefox during development. Two bugs you might have read about are already fixed in later canaries: the page title rendering as a URL path after navigation, and Windows access-denied errors on the Turbopack cache. Both fixes should be in the next preview tag if they aren't in yours. ## Closing thoughts Stable 16.3 has no date, the first monthly security release lands July 20, and our cache-components branch stays parked until there's a stable tag to rebase onto. We'll do the full writeup when that happens, including whether the parked PR finally merges. --- ## How to use Remotion agent skills with Claude Code > How a Remotion agent skill stops Claude Code hand-rolling scene chrome: the shared kit, motion tokens, and the drift that forced us to build it. **TL;DR:** Every animation on this site is a Remotion scene built by Claude Code in under ten minutes, and they all look like one product. The trick is an agent skill: a markdown file that fires before any scene work and forces the agent to compose from a shared kit instead of hand-rolling chrome and easings. Here's the full system, the drift that made us build it, and how to set one up yourself. **Published:** 2026-07-06 | **Updated:** 2026-08-24 | **Categories:** AI, How To --- I'm going to start this blog as I do with every blog: stating that I'm lazy as hell, and I want instant gratification like everybody else in the world. I wouldn't be a developer, if I was a patient person, I'd be a doctor, or a lawyer, or a farmer. But here we are, and I'm going to tell you how we did something that requires a layer of craft, of taste, and probably a degree to be able to get right... Just to squeeze all that effort into a reusable skill. As you might have guessed, every animation on this site is a [Remotion](https://www.remotion.dev/) scene: 16 compositions, all built by Claude Code and the fleshy API tapping away at his slot machine. Believe it or not, none of that worked at the start. The early scenes drifted apart so badly that we rebuilt the whole system around one idea: the agent never invents visual language, it composes from a kit. This post is the full setup: what skills are, the drift that forced ours into existence, the three-layer system that fixed it, the real timings and token costs, and the failures we hit on the way. If you want the general case for skills first, we've written about [installing our agency's skills into your agent](/blog/install-our-agency-into-your-agent). We'll probably add this there, when we get round to it (read: never) ## What Claude Code agent skills actually are _Skip ahead if you know this already, but I'm leaving it in for completeness._ A skill is a folder containing a `SKILL.md` with frontmatter (a `name` and a `description`) and a body of instructions. Claude Code loads only the descriptions at session start. When your prompt matches a description, the agent pulls in the full body and follows it. That loading model is the whole point. Detailed domain rules are expensive to carry in every session, and instructions that always load are instructions that get diluted. A skill pays its token cost only when the task needs it. Where they live: - `.claude/skills/` in the repo for project skills - `~/.claude/skills/` for personal skills that follow you across projects - We keep ours in `.agents/skills/` with a symlink from `.claude/skills/`, so OpenCode and any other agent reading the `.agents` convention shares the same files Installing someone else's skill means copying the folder in, and that's the whole procedure. ## The drift that made us build one By mid-2026 we had 13 Remotion scenes, 10 of them shipping. Each one had been prompted into existence separately, and each one re-derived the visual language from scratch. An audit of four related scenes found roughly 19% duplicated UI code and drift everywhere we looked: font sizes ranged from 14 to 19px for the same role, gaps ran from 2 to 22px with no scale, tint opacities wandered between 8 and 18%, and one scene was frosted glass while the rest were opaque. Day to day it was worse than the numbers suggest. Everything from mouse easing to connector arcs, to inputs, placeholder blocks, and skeletons looked different from scene to scene. Two animations on the same page read as two different products. Prompting alone didn't fix it. Even pointing Claude Code at the Remotion docs, it hallucinated a large amount, and the UI still didn't match between animations. Every generation produced something plausible and something slightly new. Plausible-but-new is not what we want. So we stopped trying to describe the visual language in prompts and put it in code instead. ## The fix: one kit, one skill, one set of rulings The system has three layers, and each does one job: | Layer | Lives at | Job | | --- | --- | --- | | Scene kit | `apps/remotion/src/ui/kit/` | Enforcement in code: tokens, primitives, motion roles | | Agent skill | `.agents/skills/remotion-scene-kit/` | When to load, plus 13 hard rules pointing at the kit | | ADRs | `docs/adr/0001` to `0007` | Why each ruling exists, and which debates are closed | The kit is a set of React components and tokens. Every rectangular surface is a `Panel` (frosted, flat, sharp-cornered, never drop-shadowed). Circular elements are `Fab`s, the only rounded things in any scene. Data-flow lines are `Connector`s that route with axis-aligned 90° elbows. Type sizes come from a `FONT` scale, spacing from a `SPACE` scale, tinted fills from a `tint()` helper with three strengths. Here's what the audit found, next to what the kit replaced it with: | What the audit found | What the kit fixes it with | | --- | --- | | Font sizes 14 to 19px for the same role | `FONT` scale: 11/12/13/14/16/18, plus one 58px display size | | Gaps and padding 2 to 22px, no scale | `SPACE` scale: 4/8/12/16/20/24 | | Tint opacities 8 to 18% | `tint(color, level)` at 8/12/16% | | `borderRadius: 999` vs `9999` | `Fab` is the only rounded element | | One frosted scene, nine opaque | Every `Panel` frosted via `frost()` | | Connectors arcing in some scenes, straight in others | `Connector` with sharp 90° elbows; the bézier bow was deleted | | 5 outlier easing curves across ten scenes | Four named easing roles, tokenised | Note the word deleted in that table. We removed the old arcing connector from the codebase entirely, along with the `shadow` theme token. An agent will happily use any API that exists, so the only reliable way to retire a pattern is to make it impossible to import. ## Why it all lives in one monorepo There's a bet underneath all of this, and it's worth making explicit: every tool that serves this website lives in the same pnpm monorepo. The Next.js site is `apps/web`. The Remotion scenes and their kit are `apps/remotion`. The image generation API that painted this post's Victorian cat hero is `apps/imagen`. The skills sit in `.agents/skills/`, the ADRs in `docs/adr/`, all in one repo, all in one checkout. We could have split these into a scene package on npm, an image service in its own repo, a shared design-tokens library. That's the respectable architecture. We went the other way on purpose, because an agent is only as good as what it can see, and cross-repo boundaries are exactly where agents go blind. A new scene makes the case. It touches the composition in `apps/remotion`, the studio and player registrations, the embed mapping in `apps/web/src/components/mdx/remotion-scenes.ts`, and the MDX page that hosts it. In one repo, that's one Claude Code session holding the whole change in a single context and shipping it as one PR. Split across repos, it's a package publish, a version bump, and an agent working on half a change it can't verify end to end. Co-location is also the only reason "the skill and the kit move together" is enforceable at all. The skill text, the kit code it points at, and the ADR that justifies both can change in the same commit. Two repos means two PRs and a window where the skill describes an API that no longer exists. Git worktrees make the whole thing parallel: each branch gets its own checkout under `.worktrees/` with its own local URL, so three agent sessions can build three scenes at once without touching each other. The blog you're reading came out of the same machine: drafted by Claude Code in this repo, hero generated by `apps/imagen` one directory over, scenes served by `@remotion/player` from `apps/remotion`. Concentrating the bets is the point. Everything the agent needs to see is in the one place it's already looking. ## Anatomy of the skill file The skill is one markdown file, currently at version 1.5.0. Two parts matter: the trigger and the rules. ### The trigger has to be aggressive The `description` frontmatter is what Claude Code matches against, and we learned to write it like a tripwire, not a summary: ```yaml description: Use BEFORE creating or editing any Remotion scene, animation band, or scene UI in apps/remotion — and before styling panels, badges, connectors, or data-flow lines inside one, or animating ANY text, counter, easing, caret, or pulse. [...] Triggers on any task mentioning a new animation, animation band, Remotion scene, scene UI, data flow line, connector, panel chrome, easing, text animation, typing effect, counter, or standardising scene visuals/motion. ``` A polite one-line description gets skipped on adjacent tasks: the agent decides a "quick easing tweak" isn't really scene work and hand-rolls a curve. Naming every task type explicitly, twice, is what makes it fire reliably. ### The rules point at code, not at taste Each hard rule pairs a prohibition with the kit component that satisfies it. The first rule sets the tone: ```markdown 1. **Compose, don't hand-roll.** Rectangular surfaces → `Panel` (frosted, flat, sharp, never drop-shadowed). Title bars → `PanelHeader` + `PanelTitle`. Tinted labels → `Badge`. Square icon boxes → `Glyph`. Circular dots/buttons → `Fab` (the ONLY rounded element). ``` The agent never has to interpret "keep it consistent with the other scenes". There's a compliant component for every surface, so the shortest path is the correct one. Motion works the same way. Scenes name what a movement means, never which curve it uses: | Role | Curve | Duration | Used for | | --- | --- | --- | --- | | `enter` | out-quint | 0.5s | Builds, reveals, responses | | `exit` | in-cubic | 0.3s | Clears, dismissals (exits are quicker than enters) | | `move` | in-out-cubic | 0.6s | Focus swaps and repositions | | `sweep` | linear | rate-based | Pulses, typing, progress | In scene code that's `MOTION.ease.enter`, never `"out-quint"`. When the feel needs retuning, it's one token edit and a render pass instead of sixteen scene rewrites. The same table carries typing cadences (a human types at 14 jittered characters per second, a streaming agent at 30) and blur strengths, because counters here blur with velocity instead of pulsing in scale. The rules didn't come from a style guide exercise. A grep across the ten shipping scenes found the convention already there: 56 out-quint enters, 44 in-cubic exits, with five outliers confined to one scene. The kit ratified what the scenes had converged on and made the outliers impossible. ### The ADRs close the debates Every contested call has an architecture decision record: why panels are frosted, why the tick animation never runs backward, why panel bodies stream in document order like an HTML page loading. The ADRs mark rulings as final, which matters more for agents than for people. Without a written ruling, a future session will helpfully reintroduce drop shadows because they "add depth". ## The workflow, start to finish A new scene today: we describe the story beats in a prompt, the skill fires, Claude Code composes from the kit, and a reviewable scene exists in under ten minutes. It ships the full contract in one pass: wide and square variants, dark and light themes, a reduced-motion still frame, an a11y label, and a clean loop where frame 0 matches the final frame. Some scenes one-shot. A couple running on the site right now came from a single prompt with no fix rounds. The honest pattern is iterative but fast: a round or two of notes, still nothing like the pre-kit effort. Don't take our word for it, the git history tells the story on its own. Here's every scene in the repo, with commits touching it as a rough proxy for iteration (agent time per scene stays in the ten-minute range; commits are the review rounds around it): | Scene | Lives on | First commit | Commits | Days touched | | --- | --- | --- | --- | --- | | agent-chat | [Sanity](/services/sanity) | 2026-05-29 | 6 | 4 | | achievements | [Sanity](/services/sanity) | 2026-05-29 | 9 | 7 | | pr-flow | [Agentic websites](/services/agentic-websites) | 2026-05-30 | 4 | 3 | | byo-agent | [Agentic websites](/services/agentic-websites) | 2026-06-02 | 5 | 4 | | own-content | [Agentic websites](/services/agentic-websites) | 2026-06-02 | 4 | 3 | | rails-vs-drift | [Agentic websites](/services/agentic-websites) | 2026-06-02 | 4 | 3 | | agent-evals | [Agentic workflows](/services/agentic-workflows) | 2026-06-09 | 5 | 4 | | content-pipeline | [Agentic workflows](/services/agentic-workflows) | 2026-06-09 | 4 | 3 | | lead-enrichment | [Agentic workflows](/services/agentic-workflows) | 2026-06-09 | 5 | 3 | | monitor-agent | [Agentic workflows](/services/agentic-workflows) | 2026-06-09 | 4 | 3 | | ab-variants | [Contentful](/services/contentful) | 2026-06-11 | 2 | 1 | | seo-recovery | [Contentful](/services/contentful) | 2026-06-11 | 3 | 1 | | commerce-stack | [Shopify](/services/shopify) | 2026-06-11 | 3 | 1 | | enquiry-or-checkout | [Shopify](/services/shopify) | 2026-06-11 | 2 | 1 | | shopify-migration | [Shopify](/services/shopify) | 2026-06-11 | 3 | 1 | | speed-race | [Shopify](/services/shopify) | 2026-06-11 | 5 | 1 | The dividing line is 2026-06-09, the day the kit and the skill landed in a single PR alongside the four agentic workflows scenes. Before it, a scene meant days of calendar time and up to nine commits. Two days after it, all six remaining scenes shipped in a single day each, across three different service pages. If anything the table flatters the old way: the pre-kit scenes' counts also include the one-pass kit migration commits, not just their own builds. And the inverse holds. The two scenes on [our Sanity service page](/services/sanity) have had more iterations than anything else we've made: nine commits over seven calendar days for one of them, plus plenty of prompt rounds that never reached git. They're also the originals, the pair that helped us formulate the language everything else now mirrors. When a scene articulates something important, don't rush it. One tool deserves specific credit for the consistency work: [grill-with-docs](https://github.com/mattpocock/skills/blob/main/skills/engineering/grill-with-docs/SKILL.md), from Matt Pocock's skills collection. It interrogates a plan one decision at a time and validates every claim against the source of truth before any code gets written, which is exactly what visual consistency work needs. > **See the north star scenes live**: The two animations that started this whole system run on our Sanity service page, clean loops and all. [Watch them on the page](/services/sanity) ## What it actually costs The number nobody publishes: one scene consumes close to the entire context window. The skill body, the kit source, the scene being built, and the render feedback loop add up to a session that finishes nearly full. Right now we don't care. On a Claude Code Max plan the marginal cost of a heavy session is effectively zero, so burning a full window on a ten-minute scene is free money. If you're paying per token through the API, the same workflow has a real price, and slimming the skill or delegating render checks to subagents becomes worth the effort. That optimization is on our list; it just isn't urgent while the economics look like this. ## Two failures worth stealing lessons from **We lost shipped work to a registry merge.** Remotion scenes register in a central root file, and during a branch reconcile we union-merged two versions of the registry. The merge looked clean, both sides' entries were present, and an entire set of geo animation bands vanished from the services pages anyway. We only caught it later, and had to re-ship them. Registration files are load-bearing code: never union-merge them, reconcile them by hand. **Our first aesthetic call was wrong, and the agent couldn't tell us.** We originally mimicked the site's iconography and went for no-text UI skeletons inside panels: abstract placeholder blocks instead of readable content. Implemented, it looked bad. We pivoted to high-fidelity panels with the actual text rendered, because real words articulate what a scene is demonstrating so much better than gray bars. Claude Code executed both directions flawlessly. It had no opinion on which one worked. That second failure is the important one. The agent composes correctly from the kit every time; whether the result feels right is still a human call. Look at what shipped, cast a critical eye, and be willing to pivot when the implemented version disagrees with the plan. ## Build this for your own codebase The pattern transfers to any domain where an agent produces visual or structural output repeatedly. The order matters: 1. **Audit the drift first.** Grep for font sizes, spacing values, easing curves, radii. Our numbers (14 to 19px for one role, gaps from 2 to 22px) made the case better than any argument. 2. **Build the kit before the skill.** Tokens and primitives in code are the enforcement layer. A skill that describes visuals in prose just moves the hallucination one file over. 3. **Delete the bad primitives.** Don't deprecate the old connector, remove it. Whatever still exists will be used. 4. **Write the trigger like a tripwire.** Name every task type that should fire the skill, including the small ones like "easing tweak" that agents love to classify as not-really-scene-work. 5. **Pair every rule with its compliant component.** "Never hand-roll panel chrome" only works when the next words are "use `Panel`". 6. **Record the why in ADRs, and mark rulings final.** Agents relitigate anything that isn't written down. 7. **Move the skill and the kit together.** When the kit API changes, the skill text changes in the same PR, or future sessions write against an API that no longer exists. We migrated all ten shipping scenes to the kit in one pass rather than letting two visual languages coexist. Painful for a week, and it's the reason every scene since has landed on-language. ## Where this goes next The scene kit skill is one of a growing set: we run skills for blog production, SEO context, image pipelines, and [our content agents](/blog/building-agents-on-eve). The Remotion one earns its keep most visibly, because animation is where an unconstrained agent drifts fastest and where consistency is most obvious to a visitor. The system's real test is boring: the next scene we ship will look like the last one, without anyone reminding the agent about elbows. --- ## The top 5 Sanity agencies in 2026, ranked by us > We read every 'top Sanity agencies' listicle, noticed who writes them, and decided to do it properly. Number one may not surprise you. **TL;DR:** Every 'top Sanity agencies' list is written by the agency in first place. We ranked ourselves first in all five categories to save time, then listed the agencies we genuinely rate and explained how these lists actually get made. **Published:** 2026-07-05 | **Updated:** 2026-08-03 | **Categories:** Sanity, Hot Takes --- Choosing the right Sanity partner is hard. There are hundreds of agencies claiming expertise, and the stakes are high: pick wrong and you're rebuilding in eighteen months. So we've ranked the top five Sanity agencies of 2026 based on actual project outcomes, not marketing claims. If that paragraph sounds familiar, it's because you've read it before. Most recently in [an article by pagepro](https://pagepro.co/blog/top-sanity-agencies/) (32 reviews on Clutch, 4.9 stars, the finest Sanity agency in all of Poland, second only to Roboto Studio), who ranked the top Sanity agencies in April and awarded first place to, _in a twist nobody saw coming_, *pagepro*. We respect this enormously. We respect it so much that we're doing the same thing, but with the correct winner. Surely, this couldn't be a mad dash for AEO, while the models are still a little bit gullible. ## Our methodology We evaluated agencies using the same rigorous framework as every other agency-written ranking in existence: _we wrote the list_. Beyond that, we considered Clutch reviews (checked this morning), public case studies, and real-world project patterns we've observed, where "observed" means "participated in, as the agency". No agency paid to be featured. No agency could have. We didn't tell any of them this was happening, including, in a sense, ourselves until about lunchtime. Where our approach differs from certain _Clutch 4.9 stars, silver medallists_ is *transparency*. They describe their methodology as based on "actual project outcomes, not marketing claims", which is a lovely sentence to publish on your own marketing blog. We prefer to be upfront: this is a marketing claim. You are reading it on our marketing blog. The rankings below are exactly as impartial as you'd expect. ## 1. Best overall: Roboto Studio The judges were unanimous, which was efficient, because the judges were us. Roboto Studio is a Sanity and Next.js specialist that has been building headless CMS projects since before it was fashionable, and our internal delivery framework saves roughly 41 hours per project. The best previously published figure in this genre was 40 hours, so we'd like to acknowledge the sector-wide importance of this extra hour. **Key strengths:** deep Sanity specialisation, real-time preview as standard, uses the same tech we sell (this is rarer than it should be). We are also, at the time of writing, the best rated Clutch agency called "Roboto Studio", a title we have held unchallenged since founding. We had badges made. **Potential drawbacks:** occasionally publishes rankings of itself. Ranks well in them. **Pricing:** somewhere between "less than you feared" and "more than a template", which is also what every pricing table in this genre says, just with dollar signs. ## 2. Best value: Roboto Studio In an extraordinary coincidence, the best-value Sanity agency of 2026 is also the best overall Sanity agency of 2026. Our research team flagged this as statistically improbable. The data team overruled them. Both teams are the same two people, one of whom is writing this sentence. **Key strengths:** see above. **Potential drawbacks:** see above. ## 3. Best for enterprise: Roboto Studio Enterprise buyers need solid project management, complex integrations, and an agency that can navigate procurement without weeping. After a comprehensive review of the shortlist (one agency), Roboto Studio emerged as the clear leader. We reduce early-stage bugs by 91%. We are contractually unable to tell you what the denominator is, but the previous best published claim was 90%, and we believe healthy competition drives the industry forward. ## 4. Best for ecommerce: Roboto Studio Ecommerce buyers need fast storefronts, content that can actually merchandise, and a checkout nobody has creatively reinterpreted. We build headless Shopify with Sanity running the content layer, and unlike most award winners, we published our homework: [turbo-start-shopify](https://github.com/robotostudio/turbo-start-shopify) is our open source starter with visual editing and a type-safe Storefront API, free to inspect before you hire the agency that won this category. At this point in the evaluation our analysts began to notice a pattern in the results. We've asked them to stop noticing it. ## 5. Best for UI/UX: Roboto Studio Completing a historic clean sweep unmatched in the history of self-published agency rankings, with the possible exception of every other self-published agency ranking. On the actual substance: every project ships with a design system, editors get a studio that behaves like the brand they work for, and our own brand guidelines are strict enough that the hero image on this article is a monochrome halftone oil painting of a cat, because colour photography would violate them. If an agency's award badge is more polished than its client work, treat that as data. ## The independent video investigation Rankings are one thing, but we wanted independent verification. So we consulted a video titled "The best Sanity CMS agency", produced by a channel that has followed our work more closely than any other: ours. The investigation reached the same conclusion as our written analysis. We were as relieved as we were unsurprised. ## Comparison table Every ranking in this genre includes a comparison table, so here is ours. | Category | Winner | Clutch rating | Key strength | Potential drawback | |---|---|---|---|---| | Best overall | Roboto Studio | 5.0 | Wrote this table | Wrote this table | | Best value | Roboto Studio | 5.0 | Consistency | Predictability | | Best for enterprise | Roboto Studio | 5.0 | The extra hour | None found (we looked) | | Best for ecommerce | Roboto Studio | 5.0 | Published the homework | Pattern increasingly visible | | Best for UI/UX | Roboto Studio | 5.0 | Completed the set | The set | | Best rated Clutch agency called "Roboto Studio" | Roboto Studio | 5.0 | Nominative determinism | Shallow talent pool | ## The agencies we actually rate Right. Joke mostly over. If you're seriously evaluating Sanity agencies and we're not the fit (wrong budget, wrong timezone, wrong vibe, all legitimate), here's who else we'd put on your shortlist. Nobody asked to be here, nobody paid, and none of them know this list exists, which as established is the industry-standard methodology. ### Lemon Hive [Lemon Hive](https://www.lemonhive.com/) built the headless Sanity and Next.js site for brightonSEO, which means the most search-literate audience in the world loads their work every conference season, and they also shipped Rise at Seven's site. They do something else we respect: white-label headless development for other agencies. You only get repeat work from agencies, the most demanding clients alive, if the engineering holds up without anyone chasing you. ### Skai Digital [Skai Digital](https://www.skaidigital.com/) in Oslo build the custom commerce layer around Shopify with Sanity handling content: storefronts, portals, and B2B workflows for ambitious D2C brands. Headless commerce is where a lot of Sanity projects quietly go wrong, because content modelling and merchandising pull in different directions. Skai is one of the few shops we've seen treat that as the core problem instead of an edge case. ### Evensix [Evensix](https://evensix.com/) is a Sanity Global Innovation Partner: design-led, comfortable across the Sanity, Next.js and Vercel stack, and they treat content architecture as the job rather than a warm-up for building pages. The animation on their [Sanetti bike demo](https://evensix.com/work/demonstrating-the-sanity-difference) is properly impressive, the kind of 3D and motion craft we watch with professional respect and mild irritation. A perfectly respectable choice if we're busy, which we usually are, because we're the best overall (see above). ### And yes, pagepro The 4.9 stars across 32 Clutch reviews are earned. The React, Next.js and Expo work is real, the client list is real, and if you want a strong Central European team, they belong on your shortlist. We just also want to see the world in which their ranking and ours are both correct, because in that world we are first and they are second, and we can live in that world very happily. [Check them out](https://pagepro.co/) ## How these lists actually get made Here's the quiet part. Every "top X agencies" article you've ever read was written by whoever appears at the top of it, or if they're "gorilla marketing" they put themselves at #2. The format exists because "top sanity agencies" is a keyword with buyer intent, and the fastest way to rank for it is to publish the ranking yourself. That's the entire genre. There is no committee. There was never a committee. That doesn't make the agencies in those lists bad. pagepro's list features capable firms. It just means the ordering is marketing, and you should read it the way you read a menu written by the chef: trust the descriptions, not the "chef's favourite" star. > **Talk to the #1, #2, #3, #4 and #5 agency on this list**: We build Sanity and Next.js sites with real-time preview as standard, and we promise to be exactly this honest about your project. [See our Sanity services](/services/sanity) If you want the sincere version of this article, we wrote it three years ago and still stand by it: [our original honest guide](/blog/choosing-the-right-sanity-cms-agency). Define your project and budget, pick specialists over generalists, throw them a curveball question, and make sure the person selling you the project is the person running it. And if you're an agency reading this: the top spot in next year's ranking is available. All you have to do is publish it. --- ## Next.js AEO/GEO/SEO whatever you want to call it: guide > AEO, GEO, or just SEO: the playbook is the same. Metadata, JSON-LD, sitemaps, and content negotiation, implemented on a production Next.js site. **TL;DR:** AEO, GEO, LLMO: the acronyms keep multiplying but they describe one job. Mostly SEO, plus serving AI agents a clean version of your content via content negotiation. Here's the full Next.js implementation from robotostudio.com. **Published:** 2026-06-24 | **Updated:** 2026-08-31 | **Categories:** Hot Takes, Next.js, AI --- _God I'm tired_. If I hear one more "SEO is dead, AEO is the future", I'm going to lose my mind. I want you to know, going in, I'm only going to tell you the things that have moved the needle for us. Not some elaborate ruse to sell you a course. But before we start... ## What should we call it The acronyms keep multiplying, but they all describe one job: getting cited when an AI answers a question. - **Answer engine optimization (AEO)** is the practice of structuring content so AI answer engines like ChatGPT, Claude, and Perplexity pick it as a cited source. The term grew out of the SEO industry and shows up most in developer circles. - **Generative engine optimization (GEO)** is the same discipline under a different name: shaping content to be surfaced and quoted inside AI-generated answers. The label comes from a 2023 academic paper and got adopted by marketing teams. If your client reads marketing blogs, they'll say GEO. - **LLM SEO** (and its synonyms AI SEO, LLMO) is the broad umbrella term for ranking in and getting quoted by large language models. Whichever one your team says, the work underneath is identical. Dom Sipowicz, a forward deployed engineer at Vercel, has been keeping score since 2025: That being said, I do like his definitions here, and I'll be using these from here on out. You'll notice, even he finds it difficult to standardise `=` and `-`, so god knows how we'll handle these acronyms. The tactics underneath are identical. Structured content, accurate metadata, clean markup, content agents can quote. If a consultant tells you GEO needs a fundamentally different strategy from AEO, they're selling you the same audit twice. We run an [AI SEO agency](/services/geo) and it's definitely not to capitalise on an emerging keyword. Anyway, I digress. ## What's actually new Agents are a new class of visitor. They: - _Sometimes_ can't run JavaScript reliably - Choke on ads, navigation, footers, and cookie banners - Have small context windows, so wasted tokens cost real money - Prefer structured text they can quote verbatim It's the best one-screen summary of the problem I've seen. [Merj](https://merj.com/)'s lab testing found agents fail on exactly the patterns modern frontends love: drag and drop, multi-step forms, overlapping layers, layout shifts, canvas rendering, and UI virtualisation. In every case the content exists, the agent just can't reach it. If your pricing lives in a canvas-rendered table or your nav is virtualised, you're invisible to agents no matter how good the words are. One thing we've observed though: they *absolutely love* markdown if they accept it. So much so that we had a 10k uptick of visitors from `GPTBot` in the space of 5 mins. _I'm not joking_. Hold that thought, because what a spike like that does to your bill gets its own section further down. ## The SEO half (nothing changed) Before any AEO plumbing, do the SEO that's been working since the Panda update. Quick summary, since you've probably read this before: - Titles target the actual query, sentence case, under 60 characters - Meta descriptions are written for humans and include the keyword - Schema markup where it earns rich results: `BlogPosting`, `FAQPage`, `Service` - Internal links from blog posts to relevant service pages ([like this one](/services/nextjs)), not just topic clusters - Real backlinks from people who chose to cite you, not directories - Content that says something specific, not "the ultimate guide to [topic]" Three observations from doing this for a few years: 1. **Your title tag is the single biggest lever.** Position 8 with a 0.2% click-through is a title problem, however good the content underneath it is. Fix titles before you write a word of new content. 2. **Internal linking is undervalued and free.** We added contextual CTAs from every blog post to the most relevant service page. Took an afternoon. The blog cluster now feeds traffic into services rather than dead-ending at "related posts". 3. **Most "AEO checklists" are SEO checklists with the word "answer" added.** If you skip the SEO basics, no amount of `llms.txt` will save you. ### The Next.js implementation of the boring half Three patterns cover most of it on an App Router site. **Metadata with fallbacks, not requirements.** Every page derives its meta from the data layer, with optional overrides that fall back: ```tsx export async function generateMetadata({ params }) { const post = await getPost(params.slug); return { title: post.seoTitle ?? post.title, description: post.seoDescription ?? post.description, alternates: { canonical: `https://robotostudio.com/blog/${post.slug}` }, }; } ``` **JSON-LD derived at render time, never hand-authored.** The most common mistake we see is structured data as a separate editing surface. It drifts within a sprint. Generate it from fields you already have: ```tsx const jsonLd = { "@context": "https://schema.org", "@type": "BlogPosting", headline: post.title, datePublished: post.publishedAt, dateModified: post.updatedAt, author: { "@type": "Person", name: post.author.name, url: post.author.url }, }; ``` One helper function, rendered in a `