[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-site-config":3,"$f472_H-gy6AYYAil17y3IowughHvUr5z-CyRNQmGQBnA":6,"mdc--dfyliw-key":142},{"gaMeasurementId":4,"bookingUrl":5},"G-7BYTDCVDDR","https:\u002F\u002Ftidycal.com\u002Fcmdcntr\u002F30-minute-meeting",{"post":7,"related":97},{"id":8,"slug":9,"title":10,"description":11,"summary":12,"content":13,"coverImageUrl":14,"coverImageAlt":15,"tags":16,"targetKeywords":23,"faq":28,"howTo":41,"demo":65,"featured":66,"tool":65,"status":67,"reviewNote":65,"metaTitle":10,"metaDescription":68,"canonicalUrl":69,"sourceTicketId":65,"agentGenerated":66,"publishedAt":70,"publishedBy":65,"createdAt":71,"updatedAt":71,"authorId":72,"clusterId":73,"ctaId":74,"author":75,"cluster":86,"cta":90,"tools":96},"bf3284f2-bdb2-4d08-b24b-870960743091","headless-cms-seo-complete-guide","Headless CMS SEO: The Complete Guide","The SEO concerns that genuinely change in a headless architecture, and the server-rendered Nuxt patterns we use to get metadata, canonicals, redirects, structured data, sitemaps, and Core Web Vitals right.","Headless CMS SEO comes down to one decision made well: how pages get rendered. Render server-side (SSR or SSG\u002FISR) and headless is an excellent SEO foundation; ship a client-only SPA and crawlers see an empty shell. This guide covers rendering strategy, metadata and canonicals, redirects, JSON-LD, sitemaps, Core Web Vitals, and the classic preview-content leak, with the Nuxt patterns we build on.","Headless CMS SEO comes down to one decision made well: how your pages get rendered. Metadata, canonicals, redirects, structured data, and sitemaps all follow from it. In a traditional CMS the platform renders finished HTML and hands search engines a complete page. In a headless setup the CMS only stores content and answers an API. Your front end is responsible for turning that content into HTML a crawler can read. Get the rendering strategy right and headless is very good for SEO. We build server-rendered front ends for exactly this reason. Our own site, plus a recent production Storyblok and Nuxt build, both rank on server-rendered output rather than client-side hydration. Get it wrong and you ship a fast-feeling site that Google sees as a nearly empty shell. This guide walks the concerns that genuinely change when you go headless, and the patterns we use to get each one right.\n\n## Is a headless CMS good for SEO?\n\nYes, when the front end renders on the server. Headless removes nothing search engines need. It moves the responsibility for producing crawlable HTML from the CMS to your engineering team. That is the whole trade. A headless architecture can produce faster, better-structured pages than most traditional CMS themes, because you control every byte of the output instead of fighting a theme's markup. It can also produce an SEO disaster if someone ships a client-only single-page app (SPA) and assumes Google will fill in the blanks.\n\nSo the honest answer is not \"headless is good for SEO\" or \"headless is bad for SEO.\" It is: headless is good for SEO if you render server-side, and risky if you do not. The rest of this guide assumes you want the good version and shows how to build it. If you are still weighing the architecture itself, our [headless CMS vs traditional CMS](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms) comparison covers the broader tradeoffs beyond search.\n\n![Headless moves rendering from the CMS to your front end: the CMS stores content, your Nuxt front end renders full HTML with metadata and JSON-LD, and the search engine indexes it](\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-responsibility-shift.png)\n\n## Why is rendering strategy the whole game for headless SEO?\n\nBecause a crawler indexes the HTML it receives, and your rendering strategy decides what HTML that is. There are three broad options and they are not equal for SEO.\n\n![What the crawler receives first for each rendering strategy: client-side rendering returns an empty shell, while static generation and server-side rendering both return full HTML](\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-rendering-strategies.png)\n\n**Client-side rendering (CSR).** The server sends a near-empty HTML document plus a JavaScript bundle. The browser fetches content from the CMS API and builds the page in the DOM. This is the default shape of a plain React or Vue SPA, and it is the single most common way headless SEO goes wrong. Google can execute JavaScript, but it does so on a deferred, best-effort basis. Other crawlers (Bing and most AI answer engines) are far less reliable at it. Your title, meta description, canonical, and body copy all arrive after the JavaScript runs, which means the crawler's first look is a blank page. Do not ship CSR for pages you want to rank.\n\n**Static site generation (SSG).** At build time, you fetch every piece of content from the CMS and render every page to a static HTML file. The crawler gets complete HTML instantly, and the site is cheap and fast to serve. SSG is excellent for SEO. The catch is the build. With 50,000 content items, a naive full rebuild on every edit is painful, and editors expect changes to appear quickly. Incremental static regeneration (ISR), where pages are rendered on first request and cached, closes most of that gap. We use exactly this pattern for our blog: pages render on demand and cache, so editors do not wait on a full rebuild.\n\n**Server-side rendering (SSR).** The server renders complete HTML on each request (usually with caching in front). The crawler gets a full page, and content can be fully dynamic. This is what we reach for on content-heavy and frequently-updated enterprise sites. In Nuxt this is the default mode, and it is why we lead with Nuxt for SEO-critical builds: the meta tags, the body, and the structured data are all in the initial HTML response with no JavaScript execution required.\n\nHere is the decision in one table.\n\n| Strategy | What the crawler receives first | SEO fit | Best for |\n|---|---|---|---|\n| CSR (SPA) | Empty shell, content after JS | Poor, avoid for ranking pages | App dashboards behind auth |\n| SSG \u002F ISR | Complete HTML, prebuilt or cached | Excellent | Marketing, blog, docs, mostly-static content |\n| SSR | Complete HTML per request | Excellent | Large, dynamic, frequently-updated sites |\n\nThe one rule that matters: for any page you want indexed, the meaningful content and metadata must be in the HTML the server sends, before any client JavaScript runs. Verify it by viewing source (not the inspector, which shows the hydrated DOM) or by fetching the URL with `curl` and reading the raw response. If your `\u003Ctitle>` and body copy are not in that raw HTML, you have a rendering problem no amount of on-page optimization will fix.\n\n![The same page in view-source: the server-rendered version shows the title, meta description, canonical, JSON-LD, and body copy, while the client-only version shows an empty app div](\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-view-source.png)\n\n## How do you manage titles, meta descriptions, and canonicals when the CMS no longer renders pages?\n\nIn a traditional CMS the SEO plugin writes the `\u003Ctitle>`, meta description, and canonical tag into the page for you. Headless has no page to write into, so the front end owns all of it, and it must set those tags server-side so they are present in the initial HTML.\n\nThe pattern we use in Nuxt is to pull SEO fields from the CMS as structured data, then set them with `useSeoMeta` inside the page component. Because this runs during SSR, the tags land in the server response.\n\n```ts\n\u002F\u002F pages\u002F[...slug].vue\nconst { data: page } = await useAsyncData(\n  route.path,\n  () => $cms.get(route.path),\n)\n\nuseSeoMeta({\n  title: () => page.value?.seo?.title ?? page.value?.title,\n  description: () => page.value?.seo?.description,\n  ogTitle: () => page.value?.seo?.title ?? page.value?.title,\n  ogDescription: () => page.value?.seo?.description,\n  ogImage: () => page.value?.seo?.socialImage,\n})\n\nuseHead({\n  link: [{ rel: 'canonical', href: `https:\u002F\u002Fexample.com${route.path}` }],\n})\n```\n\nTwo things we have learned to insist on here. First, model the SEO fields in the CMS as first-class content, a dedicated `seo` group with `title`, `description`, and `socialImage`, so authors control them without touching code. Both Storyblok and Contentful make this easy, and skipping it means every metadata change is a deploy.\n\nSecond, canonicals are where headless teams quietly bleed ranking signal. Because your front end may serve the same content at more than one path (trailing slash or not, uppercase or lowercase, query parameters from campaign tags), you have to pick one canonical form and emit it consistently. Derive the canonical from the CMS content's real slug, not from the request URL, so a campaign-tagged or mixed-case request still points back at the clean URL. This is the single most common canonical bug we find on headless audits.\n\n## Who owns redirects and URL structure without a monolith?\n\nThis catches teams migrating off a traditional CMS. In a monolith, the platform owned redirects: you edited them in an admin panel and the app enforced them. Headless has no such owner by default, so redirects become your responsibility and, if you forget, every changed URL becomes a 404 and every old backlink dies.\n\nYou need two things. A place to store redirects, and a layer that enforces them before the page renders. We model redirects as CMS content (a simple `from`, `to`, `statusCode` type) so non-engineers can add them, then enforce them in the front end's routing layer. In Nuxt that is server middleware:\n\n```ts\n\u002F\u002F server\u002Fmiddleware\u002Fredirects.ts\nexport default defineEventHandler(async (event) => {\n  const rule = await lookupRedirect(getRequestURL(event).pathname)\n  if (rule) {\n    return sendRedirect(event, rule.to, rule.statusCode ?? 301)\n  }\n})\n```\n\nOn URL structure, headless gives you full control, which is a gift and a trap. The gift: clean, flat, keyword-relevant paths with no `\u002Findex.php?id=42` legacy. The trap: because routing is code, it is easy to let the front-end folder structure dictate URLs rather than deriving them from the content model. Derive the path from a `fullSlug` field on the content, keep it stable, and treat any change to it as a redirect event. During a migration, export every existing indexed URL (Google Search Console and your server logs are the sources of truth) and confirm each one either still resolves or 301s. That reconciliation is the difference between a migration that holds its rankings and one that craters them.\n\n## How do you add structured data (JSON-LD) from the front end?\n\nThe same way you add everything else in headless: the front end generates it, server-side, from CMS content. No plugin injects schema behind your back, which is an advantage: you control it precisely and can drive it straight from your structured content.\n\nWe use the `nuxt-schema-org` approach: define the graph declaratively and let it render JSON-LD into the server response. For an article, that means mapping CMS fields onto the schema:\n\n```ts\nuseSchemaOrg([\n  defineArticle({\n    headline: page.value.title,\n    description: page.value.seo?.description,\n    image: page.value.coverImage,\n    datePublished: page.value.publishedAt,\n    dateModified: page.value.updatedAt,\n    author: { name: page.value.author?.name },\n  }),\n])\n```\n\nBecause JSON-LD is just structured content describing structured content, headless is a natural fit: your CMS already holds the author, the publish date, the FAQ entries. Map those fields to `Article`, `BreadcrumbList`, `Organization`, `FAQPage`, or `Product` schema and you get rich-result eligibility without hand-writing brittle JSON in a template. The rule that keeps you out of trouble: schema must describe content that is actually visible on the page. FAQ schema pointing at questions a user cannot see is a manual-action risk, not a shortcut.\n\n## How do you generate sitemaps and robots.txt from a headless source?\n\nDynamically, from the CMS, because the CMS is the only thing that knows the full set of URLs. A hand-maintained `sitemap.xml` in a headless project is stale the moment an author publishes.\n\nThe pattern is a server route that queries the CMS for every publishable content item and emits the XML. In Nuxt the `@nuxtjs\u002Fsitemap` module does this well: point it at a source that lists your URLs and it builds the sitemap at request time (or build time for SSG), including `lastmod` from the content's updated date.\n\n```ts\n\u002F\u002F server\u002Fapi\u002F__sitemap__\u002Furls.ts\nexport default defineSitemapEventHandler(async () => {\n  const items = await $cms.listPublished()\n  return items.map((item) => ({\n    loc: item.fullSlug,\n    lastmod: item.updatedAt,\n  }))\n})\n```\n\n`robots.txt` deserves specific care in headless because of the failure mode in the next section. Keep it simple: allow crawling of production and point at the sitemap. Then make absolutely sure your preview and staging environments serve a `Disallow: \u002F` robots file plus a `noindex` header. The environment split is the thing that goes wrong, so make it explicit rather than assumed.\n\n## What Core Web Vitals wins does headless make possible?\n\nReal ones, and this is where a well-built headless front end pulls ahead of a traditional theme. Because you control the entire output, you can hit Core Web Vitals targets that a plugin-laden monolith struggles to reach.\n\n- **Largest Contentful Paint (LCP).** SSR or SSG means the main content is in the initial HTML, so there is no round trip to an API before the hero renders. Pair that with proper image handling (responsive `srcset`, modern formats, and priority hints on the LCP image) and LCP gets easy. On our Nuxt builds this is where the biggest gains show up.\n- **Cumulative Layout Shift (CLS).** You control the markup, so you can reserve space for images and embeds with explicit dimensions and avoid the layout jank that theme-injected content causes.\n- **Interaction to Next Paint (INP).** A headless front end ships only the JavaScript you write, not a stack of plugin scripts. Less script means faster interaction. The discipline is to keep hydration lean and defer non-critical third-party scripts.\n- **Time to First Byte (TTFB).** Static or cached SSR served from an edge network is fast at the origin. The lever most teams miss is caching: put a CDN in front of SSR so the crawler and most users hit cache, not a cold render.\n\nNone of this is automatic. Headless gives you the ceiling, and disciplined front-end engineering is how you reach it. A headless site with unoptimized images and a bloated bundle can score worse than a decent traditional theme.\n\n## Why does preview and draft content leak to crawlers, and how do you stop it?\n\nThis is the classic headless SEO failure, and we see it more than any other. In a traditional CMS, drafts live behind the admin login and are never served publicly. In headless, your preview environment is often a real, publicly-reachable URL that renders draft content straight from the CMS with drafts enabled. If a crawler finds that URL, it indexes your unpublished content, your duplicate staging pages, and sometimes content you never intended to publish at all.\n\nIt leaks through a few doors. A preview deploy on a guessable subdomain with no `noindex`. Draft mode toggled by a query parameter that a crawler stumbles into. A `sitemap.xml` on staging that lists real URLs. An internal link to a preview URL that gets crawled.\n\nThe fixes are boring and they work.\n\n1. Serve `X-Robots-Tag: noindex, nofollow` on every non-production environment, at the edge or in server middleware, not per-page where it is easy to miss.\n2. Serve `Disallow: \u002F` in `robots.txt` on preview and staging.\n3. Put preview behind authentication (a shared password or an access gate) so it is not publicly fetchable at all. This is the strongest control.\n4. Ensure your API's \"include drafts\" flag is driven by an environment variable that is false in production and cannot be flipped by a query parameter.\n5. Never let a staging environment emit a public sitemap.\n\nThe draft-leak problem overlaps with headless security more broadly, because the same misconfigurations that expose drafts to Google often expose your CMS API surface too. We cover that surface in [headless CMS security](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-security). For SEO specifically, treat \"can a crawler reach a non-production URL\" as a launch-blocking checklist item, not a nice-to-have.\n\n## Headless CMS SEO best practices, in short\n\nIf you take nothing else from this guide, take these.\n\n- Render server-side (SSR or SSG\u002FISR) for every page you want to rank. Verify with view-source or `curl`, not the inspector.\n- Set titles, descriptions, and canonicals server-side from CMS-modeled SEO fields, and derive canonicals from the content slug, not the request URL.\n- Own your redirects deliberately: store them, enforce them before render, and reconcile every old URL during a migration.\n- Generate sitemaps dynamically from the CMS with real `lastmod` values.\n- Use the ceiling headless gives you on Core Web Vitals, and do the image and bundle work to actually reach it.\n- Lock down preview and staging so drafts never reach a crawler.\n\nHandled with that discipline, a headless build is one of the best SEO foundations you can have, which is exactly why we build SSR-first front ends for content that has to be found. If you are choosing a stack, migrating off a monolith, or trying to figure out why a headless site is not ranking, that is the work we do. See our [headless CMS agency](\u002Fservices\u002Fheadless-cms) practice, or if you are on the enterprise Digital Experience Platform (DXP) side, our [Sitecore development and support](\u002Fservices\u002Fsitecore) work. If you want a second set of eyes on a specific build, [book a call](\u002Fbook) and we will look at the actual rendered output.\n\n## Read next\n\n- [Headless CMS vs Traditional CMS](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms): the full architecture tradeoff beyond search.\n- [Headless CMS Security](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-security): the API-surface and preview-environment risks that overlap with the draft-leak problem here.\n- [Enterprise CMS in 2026: Platform and Partner](\u002Fblog\u002Fenterprise-cms\u002Fenterprise-cms-2026-platform-and-partner): how to choose the platform and who delivers it.\n- [Enterprise Headless CMS, Explained](\u002Fblog\u002Fenterprise-cms\u002Fenterprise-headless-cms-explained): what \"enterprise\" adds on top of a basic headless setup.\n- [Headless CMS Localization](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-localization): multi-language and multi-region content without breaking SEO.\n- Work with us: our [headless CMS agency](\u002Fservices\u002Fheadless-cms) practice, or [Sitecore development and support](\u002Fservices\u002Fsitecore).","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-seo.png","Chrome and electric-violet diagram of a headless CMS feeding content through an API into a server-rendered front end, with SEO tags and structured data in the HTML response.",[17,18,19,20,21,22],"headless cms","seo","nuxt","core web vitals","structured data","ssr",[24,25,26,27],"headless cms seo","seo for headless cms","is headless cms good for seo","headless cms seo best practices",[29,32,35,38],{"answer":30,"question":31},"Yes, as long as the front end renders on the server. Headless removes nothing search engines need. It moves responsibility for producing crawlable HTML from the CMS to your engineers. Render with SSR or static generation and headless is excellent for SEO. Ship a client-only app and crawlers see a blank page.","Is a headless CMS good for SEO?",{"answer":33,"question":34},"Because a crawler indexes the HTML it receives first, and your rendering strategy decides what that HTML contains. Server-side rendering and static generation put full content and metadata in the initial response. Client-side rendering delivers an empty shell that only fills in after JavaScript runs, which many crawlers handle poorly or not at all.","Why is rendering strategy the most important part of headless SEO?",{"answer":36,"question":37},"The front end sets them server-side from SEO fields modeled in the CMS. In Nuxt we pull a dedicated seo group and apply it with useSeoMeta during SSR so the tags appear in the initial HTML. Derive canonicals from the content slug, not the request URL, to avoid duplicate-path signal loss.","How do you set titles, meta descriptions, and canonicals in a headless build?",{"answer":39,"question":40},"Preview and staging in headless are often real public URLs that render draft content with drafts enabled. If a crawler finds one, it indexes unpublished pages. Stop it by serving noindex headers and a Disallow robots file on every non-production environment, gating preview behind authentication, and never emitting a staging sitemap.","Why does draft content leak to Google on headless sites?",{"name":42,"steps":43},"Set up SEO on a headless CMS build",[44,47,50,53,56,59,62],{"name":45,"text":46},"Choose a server-rendered strategy","Pick SSR or SSG\u002FISR for every page you want to rank, and reserve client-only rendering for authenticated app views. Verify the choice with view-source or a curl request, not the browser inspector, so you confirm the crawler receives full HTML.",{"name":48,"text":49},"Model SEO fields as content","Add a dedicated seo group in the CMS with title, description, and social image so authors control metadata without a deploy. Apply the fields server-side (useSeoMeta in Nuxt) so the tags land in the initial response.",{"name":51,"text":52},"Fix canonicals to the content slug","Derive the canonical URL from the content's real slug rather than the incoming request path, so mixed-case, trailing-slash, and campaign-tagged variants all point back at one clean URL.",{"name":54,"text":55},"Own redirects deliberately","Store redirects as CMS content and enforce them in server middleware before the page renders. During a migration, export every indexed URL from Search Console and server logs and confirm each one resolves or 301s.",{"name":57,"text":58},"Emit JSON-LD and sitemaps from the CMS","Generate structured data server-side from content fields (article, breadcrumb, FAQ) and build the sitemap dynamically from the list of published items with real lastmod dates.",{"name":60,"text":61},"Lock down preview environments","Serve noindex headers and a Disallow robots file on all non-production environments, gate preview behind authentication, and drive the include-drafts flag from an environment variable that is false in production.",{"name":63,"text":64},"Tune Core Web Vitals","Use the headless ceiling: prioritize the LCP image, reserve space to avoid layout shift, ship only the JavaScript you write, and put a CDN in front of SSR so most requests hit cache.",null,false,"published","Headless CMS SEO is one decision made well: render server-side. Metadata, canonicals, redirects, JSON-LD, sitemaps, and Core Web Vitals, with Nuxt patterns.","https:\u002F\u002Fcmdcntr.io\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-seo-complete-guide","2026-07-14T14:00:00.000Z","2026-07-14T23:50:41.153Z","ed80da88-f3d1-4aa9-9373-18c39fecb740","23d49753-baf2-4629-b6fc-df27c558f48d","c0fcf6a1-58d2-4262-8996-3ad29d58eea5",{"slug":76,"name":77,"avatarUrl":78,"role":79,"bio":80,"isPublic":81,"expertiseSummary":82,"linkedin":83,"github":84,"twitter":65,"website":85},"michael-graham","Michael Graham","\u002Fapi\u002Fpublic\u002Favatar\u002F7452d3cc-b0aa-46e4-96e6-da9c0225c471","Founder & Software Engineer","Obsessed with building top-tier web software and crafting unique, polished user experiences.",true,"From frontend design to a fully deployed application, I can build the entire pipeline.","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmikemartire\u002F","https:\u002F\u002Fgithub.com\u002Fmichaelchristophergraham","https:\u002F\u002Fcmdcntr.io",{"slug":87,"name":88,"color":89},"enterprise-cms","Enterprise CMS Delivery","#0EA5E9",{"id":74,"name":91,"ctaType":92,"heading":93,"description":94,"buttonText":95,"url":5,"leadMagnetUrl":65},"Book a Call (blog)","book_call","Shipping AI-generated code that keeps breaking?","Book a free 30-minute call with a senior engineer. We diagnose what is going wrong and give you a concrete fix plan, no obligation.","Book a free call",[],[98,113,128],{"id":99,"slug":100,"title":101,"description":102,"coverImageUrl":103,"coverImageAlt":104,"tags":105,"featured":66,"tool":65,"publishedAt":110,"author":111,"cluster":112},"69f3e275-8bdb-40dc-ac93-b0e87af05d2f","headless-cms-localization","Headless CMS Localization: Patterns That Scale","How localization actually works in a headless CMS: field-level vs entry-level vs space-level translation, locale URL structure and hreflang, fallbacks, RTL, and keeping Nuxt i18n in sync with the CMS, with first-hand Storyblok and Sitecore notes.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-localization.png","A polished chrome globe wrapped in glowing purple circuitry branching into multiple language routes",[17,106,107,108,109,19],"localization","i18n","storyblok","sitecore","2026-07-12T14:00:00.000Z",{"slug":76,"name":77,"avatarUrl":78,"role":79,"bio":80,"isPublic":81},{"slug":87,"name":88,"color":89},{"id":114,"slug":115,"title":116,"description":117,"coverImageUrl":118,"coverImageAlt":119,"tags":120,"featured":66,"tool":65,"publishedAt":125,"author":126,"cluster":127},"eb994797-29e0-4ad6-8aaf-01f353cd8410","headless-cms-security","Headless CMS Security: The Real Attack Surface","A headless CMS is secure when you treat it as one, but decoupling moves the attack surface to the seams: API tokens, preview endpoints, webhooks, edge caching, rich-text XSS, RBAC, and front-end secrets. Here is the real surface and a hardening checklist.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-security.png","A polished chrome content block splitting apart with glowing electric-violet API connections and a lock symbol at each exposed seam",[17,121,122,123,124],"cms security","api security","web security","enterprise cms","2026-07-11T14:00:00.000Z",{"slug":76,"name":77,"avatarUrl":78,"role":79,"bio":80,"isPublic":81},{"slug":87,"name":88,"color":89},{"id":129,"slug":130,"title":131,"description":132,"coverImageUrl":133,"coverImageAlt":134,"tags":135,"featured":66,"tool":65,"publishedAt":139,"author":140,"cluster":141},"a5765020-18cd-4000-b24f-4df388d7eb03","headless-cms-vs-traditional-cms","Headless CMS vs Traditional CMS: How to Actually Choose","A decision framework for headless CMS vs traditional CMS: the honest tradeoffs, a situation-keyed decision table, and the real Sanity vs Storyblok vs Strapi call we made on a production Nuxt build.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-vs-traditional.png","A polished chrome CMS block splitting into two halves, one rendering its own page and one streaming content through a glowing electric-violet API channel to a separate front end",[17,136,137,109,108,138],"traditional cms","dxp","cms selection","2026-07-10T14:00:00.000Z",{"slug":76,"name":77,"avatarUrl":78,"role":79,"bio":80,"isPublic":81},{"slug":87,"name":88,"color":89},{"data":143,"body":144},{},{"type":145,"children":146},"root",[147,155,161,166,180,189,195,200,208,219,229,239,244,350,372,380,386,398,411,1080,1113,1118,1124,1129,1156,1396,1417,1423,1428,1441,1752,1794,1800,1813,1834,2053,2080,2086,2091,2144,2149,2155,2160,2179,2184,2234,2247,2253,2258,2305,2334,2340,2412],{"type":148,"tag":149,"props":150,"children":151},"element","p",{},[152],{"type":153,"value":154},"text","Headless CMS SEO comes down to one decision made well: how your pages get rendered. Metadata, canonicals, redirects, structured data, and sitemaps all follow from it. In a traditional CMS the platform renders finished HTML and hands search engines a complete page. In a headless setup the CMS only stores content and answers an API. Your front end is responsible for turning that content into HTML a crawler can read. Get the rendering strategy right and headless is very good for SEO. We build server-rendered front ends for exactly this reason. Our own site, plus a recent production Storyblok and Nuxt build, both rank on server-rendered output rather than client-side hydration. Get it wrong and you ship a fast-feeling site that Google sees as a nearly empty shell. This guide walks the concerns that genuinely change when you go headless, and the patterns we use to get each one right.",{"type":148,"tag":156,"props":157,"children":159},"h2",{"id":158},"is-a-headless-cms-good-for-seo",[160],{"type":153,"value":31},{"type":148,"tag":149,"props":162,"children":163},{},[164],{"type":153,"value":165},"Yes, when the front end renders on the server. Headless removes nothing search engines need. It moves the responsibility for producing crawlable HTML from the CMS to your engineering team. That is the whole trade. A headless architecture can produce faster, better-structured pages than most traditional CMS themes, because you control every byte of the output instead of fighting a theme's markup. It can also produce an SEO disaster if someone ships a client-only single-page app (SPA) and assumes Google will fill in the blanks.",{"type":148,"tag":149,"props":167,"children":168},{},[169,171,178],{"type":153,"value":170},"So the honest answer is not \"headless is good for SEO\" or \"headless is bad for SEO.\" It is: headless is good for SEO if you render server-side, and risky if you do not. The rest of this guide assumes you want the good version and shows how to build it. If you are still weighing the architecture itself, our ",{"type":148,"tag":172,"props":173,"children":175},"a",{"href":174},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms",[176],{"type":153,"value":177},"headless CMS vs traditional CMS",{"type":153,"value":179}," comparison covers the broader tradeoffs beyond search.",{"type":148,"tag":149,"props":181,"children":182},{},[183],{"type":148,"tag":184,"props":185,"children":188},"img",{"alt":186,"src":187},"Headless moves rendering from the CMS to your front end: the CMS stores content, your Nuxt front end renders full HTML with metadata and JSON-LD, and the search engine indexes it","\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-responsibility-shift.png",[],{"type":148,"tag":156,"props":190,"children":192},{"id":191},"why-is-rendering-strategy-the-whole-game-for-headless-seo",[193],{"type":153,"value":194},"Why is rendering strategy the whole game for headless SEO?",{"type":148,"tag":149,"props":196,"children":197},{},[198],{"type":153,"value":199},"Because a crawler indexes the HTML it receives, and your rendering strategy decides what HTML that is. There are three broad options and they are not equal for SEO.",{"type":148,"tag":149,"props":201,"children":202},{},[203],{"type":148,"tag":184,"props":204,"children":207},{"alt":205,"src":206},"What the crawler receives first for each rendering strategy: client-side rendering returns an empty shell, while static generation and server-side rendering both return full HTML","\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-rendering-strategies.png",[],{"type":148,"tag":149,"props":209,"children":210},{},[211,217],{"type":148,"tag":212,"props":213,"children":214},"strong",{},[215],{"type":153,"value":216},"Client-side rendering (CSR).",{"type":153,"value":218}," The server sends a near-empty HTML document plus a JavaScript bundle. The browser fetches content from the CMS API and builds the page in the DOM. This is the default shape of a plain React or Vue SPA, and it is the single most common way headless SEO goes wrong. Google can execute JavaScript, but it does so on a deferred, best-effort basis. Other crawlers (Bing and most AI answer engines) are far less reliable at it. Your title, meta description, canonical, and body copy all arrive after the JavaScript runs, which means the crawler's first look is a blank page. Do not ship CSR for pages you want to rank.",{"type":148,"tag":149,"props":220,"children":221},{},[222,227],{"type":148,"tag":212,"props":223,"children":224},{},[225],{"type":153,"value":226},"Static site generation (SSG).",{"type":153,"value":228}," At build time, you fetch every piece of content from the CMS and render every page to a static HTML file. The crawler gets complete HTML instantly, and the site is cheap and fast to serve. SSG is excellent for SEO. The catch is the build. With 50,000 content items, a naive full rebuild on every edit is painful, and editors expect changes to appear quickly. Incremental static regeneration (ISR), where pages are rendered on first request and cached, closes most of that gap. We use exactly this pattern for our blog: pages render on demand and cache, so editors do not wait on a full rebuild.",{"type":148,"tag":149,"props":230,"children":231},{},[232,237],{"type":148,"tag":212,"props":233,"children":234},{},[235],{"type":153,"value":236},"Server-side rendering (SSR).",{"type":153,"value":238}," The server renders complete HTML on each request (usually with caching in front). The crawler gets a full page, and content can be fully dynamic. This is what we reach for on content-heavy and frequently-updated enterprise sites. In Nuxt this is the default mode, and it is why we lead with Nuxt for SEO-critical builds: the meta tags, the body, and the structured data are all in the initial HTML response with no JavaScript execution required.",{"type":148,"tag":149,"props":240,"children":241},{},[242],{"type":153,"value":243},"Here is the decision in one table.",{"type":148,"tag":245,"props":246,"children":247},"table",{},[248,277],{"type":148,"tag":249,"props":250,"children":251},"thead",{},[252],{"type":148,"tag":253,"props":254,"children":255},"tr",{},[256,262,267,272],{"type":148,"tag":257,"props":258,"children":259},"th",{},[260],{"type":153,"value":261},"Strategy",{"type":148,"tag":257,"props":263,"children":264},{},[265],{"type":153,"value":266},"What the crawler receives first",{"type":148,"tag":257,"props":268,"children":269},{},[270],{"type":153,"value":271},"SEO fit",{"type":148,"tag":257,"props":273,"children":274},{},[275],{"type":153,"value":276},"Best for",{"type":148,"tag":278,"props":279,"children":280},"tbody",{},[281,305,328],{"type":148,"tag":253,"props":282,"children":283},{},[284,290,295,300],{"type":148,"tag":285,"props":286,"children":287},"td",{},[288],{"type":153,"value":289},"CSR (SPA)",{"type":148,"tag":285,"props":291,"children":292},{},[293],{"type":153,"value":294},"Empty shell, content after JS",{"type":148,"tag":285,"props":296,"children":297},{},[298],{"type":153,"value":299},"Poor, avoid for ranking pages",{"type":148,"tag":285,"props":301,"children":302},{},[303],{"type":153,"value":304},"App dashboards behind auth",{"type":148,"tag":253,"props":306,"children":307},{},[308,313,318,323],{"type":148,"tag":285,"props":309,"children":310},{},[311],{"type":153,"value":312},"SSG \u002F ISR",{"type":148,"tag":285,"props":314,"children":315},{},[316],{"type":153,"value":317},"Complete HTML, prebuilt or cached",{"type":148,"tag":285,"props":319,"children":320},{},[321],{"type":153,"value":322},"Excellent",{"type":148,"tag":285,"props":324,"children":325},{},[326],{"type":153,"value":327},"Marketing, blog, docs, mostly-static content",{"type":148,"tag":253,"props":329,"children":330},{},[331,336,341,345],{"type":148,"tag":285,"props":332,"children":333},{},[334],{"type":153,"value":335},"SSR",{"type":148,"tag":285,"props":337,"children":338},{},[339],{"type":153,"value":340},"Complete HTML per request",{"type":148,"tag":285,"props":342,"children":343},{},[344],{"type":153,"value":322},{"type":148,"tag":285,"props":346,"children":347},{},[348],{"type":153,"value":349},"Large, dynamic, frequently-updated sites",{"type":148,"tag":149,"props":351,"children":352},{},[353,355,362,364,370],{"type":153,"value":354},"The one rule that matters: for any page you want indexed, the meaningful content and metadata must be in the HTML the server sends, before any client JavaScript runs. Verify it by viewing source (not the inspector, which shows the hydrated DOM) or by fetching the URL with ",{"type":148,"tag":356,"props":357,"children":359},"code",{"className":358},[],[360],{"type":153,"value":361},"curl",{"type":153,"value":363}," and reading the raw response. If your ",{"type":148,"tag":356,"props":365,"children":367},{"className":366},[],[368],{"type":153,"value":369},"\u003Ctitle>",{"type":153,"value":371}," and body copy are not in that raw HTML, you have a rendering problem no amount of on-page optimization will fix.",{"type":148,"tag":149,"props":373,"children":374},{},[375],{"type":148,"tag":184,"props":376,"children":379},{"alt":377,"src":378},"The same page in view-source: the server-rendered version shows the title, meta description, canonical, JSON-LD, and body copy, while the client-only version shows an empty app div","\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Fseo-view-source.png",[],{"type":148,"tag":156,"props":381,"children":383},{"id":382},"how-do-you-manage-titles-meta-descriptions-and-canonicals-when-the-cms-no-longer-renders-pages",[384],{"type":153,"value":385},"How do you manage titles, meta descriptions, and canonicals when the CMS no longer renders pages?",{"type":148,"tag":149,"props":387,"children":388},{},[389,391,396],{"type":153,"value":390},"In a traditional CMS the SEO plugin writes the ",{"type":148,"tag":356,"props":392,"children":394},{"className":393},[],[395],{"type":153,"value":369},{"type":153,"value":397},", meta description, and canonical tag into the page for you. Headless has no page to write into, so the front end owns all of it, and it must set those tags server-side so they are present in the initial HTML.",{"type":148,"tag":149,"props":399,"children":400},{},[401,403,409],{"type":153,"value":402},"The pattern we use in Nuxt is to pull SEO fields from the CMS as structured data, then set them with ",{"type":148,"tag":356,"props":404,"children":406},{"className":405},[],[407],{"type":153,"value":408},"useSeoMeta",{"type":153,"value":410}," inside the page component. Because this runs during SSR, the tags land in the server response.",{"type":148,"tag":412,"props":413,"children":418},"pre",{"className":414,"code":415,"language":416,"meta":417,"style":417},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F pages\u002F[...slug].vue\nconst { data: page } = await useAsyncData(\n  route.path,\n  () => $cms.get(route.path),\n)\n\nuseSeoMeta({\n  title: () => page.value?.seo?.title ?? page.value?.title,\n  description: () => page.value?.seo?.description,\n  ogTitle: () => page.value?.seo?.title ?? page.value?.title,\n  ogDescription: () => page.value?.seo?.description,\n  ogImage: () => page.value?.seo?.socialImage,\n})\n\nuseHead({\n  link: [{ rel: 'canonical', href: `https:\u002F\u002Fexample.com${route.path}` }],\n})\n","ts","",[419],{"type":148,"tag":356,"props":420,"children":421},{"__ignoreMap":417},[422,434,494,518,564,573,582,600,684,738,815,868,922,934,942,959,1068],{"type":148,"tag":423,"props":424,"children":427},"span",{"class":425,"line":426},"line",1,[428],{"type":148,"tag":423,"props":429,"children":431},{"style":430},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[432],{"type":153,"value":433},"\u002F\u002F pages\u002F[...slug].vue\n",{"type":148,"tag":423,"props":435,"children":437},{"class":425,"line":436},2,[438,444,450,456,461,467,472,477,483,489],{"type":148,"tag":423,"props":439,"children":441},{"style":440},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[442],{"type":153,"value":443},"const",{"type":148,"tag":423,"props":445,"children":447},{"style":446},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[448],{"type":153,"value":449}," {",{"type":148,"tag":423,"props":451,"children":453},{"style":452},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[454],{"type":153,"value":455}," data",{"type":148,"tag":423,"props":457,"children":458},{"style":446},[459],{"type":153,"value":460},":",{"type":148,"tag":423,"props":462,"children":464},{"style":463},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[465],{"type":153,"value":466}," page ",{"type":148,"tag":423,"props":468,"children":469},{"style":446},[470],{"type":153,"value":471},"}",{"type":148,"tag":423,"props":473,"children":474},{"style":446},[475],{"type":153,"value":476}," =",{"type":148,"tag":423,"props":478,"children":480},{"style":479},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[481],{"type":153,"value":482}," await",{"type":148,"tag":423,"props":484,"children":486},{"style":485},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[487],{"type":153,"value":488}," useAsyncData",{"type":148,"tag":423,"props":490,"children":491},{"style":463},[492],{"type":153,"value":493},"(\n",{"type":148,"tag":423,"props":495,"children":497},{"class":425,"line":496},3,[498,503,508,513],{"type":148,"tag":423,"props":499,"children":500},{"style":463},[501],{"type":153,"value":502},"  route",{"type":148,"tag":423,"props":504,"children":505},{"style":446},[506],{"type":153,"value":507},".",{"type":148,"tag":423,"props":509,"children":510},{"style":463},[511],{"type":153,"value":512},"path",{"type":148,"tag":423,"props":514,"children":515},{"style":446},[516],{"type":153,"value":517},",\n",{"type":148,"tag":423,"props":519,"children":521},{"class":425,"line":520},4,[522,527,532,537,541,546,551,555,560],{"type":148,"tag":423,"props":523,"children":524},{"style":446},[525],{"type":153,"value":526},"  ()",{"type":148,"tag":423,"props":528,"children":529},{"style":440},[530],{"type":153,"value":531}," =>",{"type":148,"tag":423,"props":533,"children":534},{"style":463},[535],{"type":153,"value":536}," $cms",{"type":148,"tag":423,"props":538,"children":539},{"style":446},[540],{"type":153,"value":507},{"type":148,"tag":423,"props":542,"children":543},{"style":485},[544],{"type":153,"value":545},"get",{"type":148,"tag":423,"props":547,"children":548},{"style":463},[549],{"type":153,"value":550},"(route",{"type":148,"tag":423,"props":552,"children":553},{"style":446},[554],{"type":153,"value":507},{"type":148,"tag":423,"props":556,"children":557},{"style":463},[558],{"type":153,"value":559},"path)",{"type":148,"tag":423,"props":561,"children":562},{"style":446},[563],{"type":153,"value":517},{"type":148,"tag":423,"props":565,"children":567},{"class":425,"line":566},5,[568],{"type":148,"tag":423,"props":569,"children":570},{"style":463},[571],{"type":153,"value":572},")\n",{"type":148,"tag":423,"props":574,"children":576},{"class":425,"line":575},6,[577],{"type":148,"tag":423,"props":578,"children":579},{"emptyLinePlaceholder":81},[580],{"type":153,"value":581},"\n",{"type":148,"tag":423,"props":583,"children":585},{"class":425,"line":584},7,[586,590,595],{"type":148,"tag":423,"props":587,"children":588},{"style":485},[589],{"type":153,"value":408},{"type":148,"tag":423,"props":591,"children":592},{"style":463},[593],{"type":153,"value":594},"(",{"type":148,"tag":423,"props":596,"children":597},{"style":446},[598],{"type":153,"value":599},"{\n",{"type":148,"tag":423,"props":601,"children":603},{"class":425,"line":602},8,[604,609,613,618,622,627,631,636,641,645,649,654,659,663,667,671,675,680],{"type":148,"tag":423,"props":605,"children":606},{"style":485},[607],{"type":153,"value":608},"  title",{"type":148,"tag":423,"props":610,"children":611},{"style":446},[612],{"type":153,"value":460},{"type":148,"tag":423,"props":614,"children":615},{"style":446},[616],{"type":153,"value":617}," ()",{"type":148,"tag":423,"props":619,"children":620},{"style":440},[621],{"type":153,"value":531},{"type":148,"tag":423,"props":623,"children":624},{"style":463},[625],{"type":153,"value":626}," page",{"type":148,"tag":423,"props":628,"children":629},{"style":446},[630],{"type":153,"value":507},{"type":148,"tag":423,"props":632,"children":633},{"style":463},[634],{"type":153,"value":635},"value",{"type":148,"tag":423,"props":637,"children":638},{"style":446},[639],{"type":153,"value":640},"?.",{"type":148,"tag":423,"props":642,"children":643},{"style":463},[644],{"type":153,"value":18},{"type":148,"tag":423,"props":646,"children":647},{"style":446},[648],{"type":153,"value":640},{"type":148,"tag":423,"props":650,"children":651},{"style":463},[652],{"type":153,"value":653},"title ",{"type":148,"tag":423,"props":655,"children":656},{"style":446},[657],{"type":153,"value":658},"??",{"type":148,"tag":423,"props":660,"children":661},{"style":463},[662],{"type":153,"value":626},{"type":148,"tag":423,"props":664,"children":665},{"style":446},[666],{"type":153,"value":507},{"type":148,"tag":423,"props":668,"children":669},{"style":463},[670],{"type":153,"value":635},{"type":148,"tag":423,"props":672,"children":673},{"style":446},[674],{"type":153,"value":640},{"type":148,"tag":423,"props":676,"children":677},{"style":463},[678],{"type":153,"value":679},"title",{"type":148,"tag":423,"props":681,"children":682},{"style":446},[683],{"type":153,"value":517},{"type":148,"tag":423,"props":685,"children":687},{"class":425,"line":686},9,[688,693,697,701,705,709,713,717,721,725,729,734],{"type":148,"tag":423,"props":689,"children":690},{"style":485},[691],{"type":153,"value":692},"  description",{"type":148,"tag":423,"props":694,"children":695},{"style":446},[696],{"type":153,"value":460},{"type":148,"tag":423,"props":698,"children":699},{"style":446},[700],{"type":153,"value":617},{"type":148,"tag":423,"props":702,"children":703},{"style":440},[704],{"type":153,"value":531},{"type":148,"tag":423,"props":706,"children":707},{"style":463},[708],{"type":153,"value":626},{"type":148,"tag":423,"props":710,"children":711},{"style":446},[712],{"type":153,"value":507},{"type":148,"tag":423,"props":714,"children":715},{"style":463},[716],{"type":153,"value":635},{"type":148,"tag":423,"props":718,"children":719},{"style":446},[720],{"type":153,"value":640},{"type":148,"tag":423,"props":722,"children":723},{"style":463},[724],{"type":153,"value":18},{"type":148,"tag":423,"props":726,"children":727},{"style":446},[728],{"type":153,"value":640},{"type":148,"tag":423,"props":730,"children":731},{"style":463},[732],{"type":153,"value":733},"description",{"type":148,"tag":423,"props":735,"children":736},{"style":446},[737],{"type":153,"value":517},{"type":148,"tag":423,"props":739,"children":741},{"class":425,"line":740},10,[742,747,751,755,759,763,767,771,775,779,783,787,791,795,799,803,807,811],{"type":148,"tag":423,"props":743,"children":744},{"style":485},[745],{"type":153,"value":746},"  ogTitle",{"type":148,"tag":423,"props":748,"children":749},{"style":446},[750],{"type":153,"value":460},{"type":148,"tag":423,"props":752,"children":753},{"style":446},[754],{"type":153,"value":617},{"type":148,"tag":423,"props":756,"children":757},{"style":440},[758],{"type":153,"value":531},{"type":148,"tag":423,"props":760,"children":761},{"style":463},[762],{"type":153,"value":626},{"type":148,"tag":423,"props":764,"children":765},{"style":446},[766],{"type":153,"value":507},{"type":148,"tag":423,"props":768,"children":769},{"style":463},[770],{"type":153,"value":635},{"type":148,"tag":423,"props":772,"children":773},{"style":446},[774],{"type":153,"value":640},{"type":148,"tag":423,"props":776,"children":777},{"style":463},[778],{"type":153,"value":18},{"type":148,"tag":423,"props":780,"children":781},{"style":446},[782],{"type":153,"value":640},{"type":148,"tag":423,"props":784,"children":785},{"style":463},[786],{"type":153,"value":653},{"type":148,"tag":423,"props":788,"children":789},{"style":446},[790],{"type":153,"value":658},{"type":148,"tag":423,"props":792,"children":793},{"style":463},[794],{"type":153,"value":626},{"type":148,"tag":423,"props":796,"children":797},{"style":446},[798],{"type":153,"value":507},{"type":148,"tag":423,"props":800,"children":801},{"style":463},[802],{"type":153,"value":635},{"type":148,"tag":423,"props":804,"children":805},{"style":446},[806],{"type":153,"value":640},{"type":148,"tag":423,"props":808,"children":809},{"style":463},[810],{"type":153,"value":679},{"type":148,"tag":423,"props":812,"children":813},{"style":446},[814],{"type":153,"value":517},{"type":148,"tag":423,"props":816,"children":818},{"class":425,"line":817},11,[819,824,828,832,836,840,844,848,852,856,860,864],{"type":148,"tag":423,"props":820,"children":821},{"style":485},[822],{"type":153,"value":823},"  ogDescription",{"type":148,"tag":423,"props":825,"children":826},{"style":446},[827],{"type":153,"value":460},{"type":148,"tag":423,"props":829,"children":830},{"style":446},[831],{"type":153,"value":617},{"type":148,"tag":423,"props":833,"children":834},{"style":440},[835],{"type":153,"value":531},{"type":148,"tag":423,"props":837,"children":838},{"style":463},[839],{"type":153,"value":626},{"type":148,"tag":423,"props":841,"children":842},{"style":446},[843],{"type":153,"value":507},{"type":148,"tag":423,"props":845,"children":846},{"style":463},[847],{"type":153,"value":635},{"type":148,"tag":423,"props":849,"children":850},{"style":446},[851],{"type":153,"value":640},{"type":148,"tag":423,"props":853,"children":854},{"style":463},[855],{"type":153,"value":18},{"type":148,"tag":423,"props":857,"children":858},{"style":446},[859],{"type":153,"value":640},{"type":148,"tag":423,"props":861,"children":862},{"style":463},[863],{"type":153,"value":733},{"type":148,"tag":423,"props":865,"children":866},{"style":446},[867],{"type":153,"value":517},{"type":148,"tag":423,"props":869,"children":871},{"class":425,"line":870},12,[872,877,881,885,889,893,897,901,905,909,913,918],{"type":148,"tag":423,"props":873,"children":874},{"style":485},[875],{"type":153,"value":876},"  ogImage",{"type":148,"tag":423,"props":878,"children":879},{"style":446},[880],{"type":153,"value":460},{"type":148,"tag":423,"props":882,"children":883},{"style":446},[884],{"type":153,"value":617},{"type":148,"tag":423,"props":886,"children":887},{"style":440},[888],{"type":153,"value":531},{"type":148,"tag":423,"props":890,"children":891},{"style":463},[892],{"type":153,"value":626},{"type":148,"tag":423,"props":894,"children":895},{"style":446},[896],{"type":153,"value":507},{"type":148,"tag":423,"props":898,"children":899},{"style":463},[900],{"type":153,"value":635},{"type":148,"tag":423,"props":902,"children":903},{"style":446},[904],{"type":153,"value":640},{"type":148,"tag":423,"props":906,"children":907},{"style":463},[908],{"type":153,"value":18},{"type":148,"tag":423,"props":910,"children":911},{"style":446},[912],{"type":153,"value":640},{"type":148,"tag":423,"props":914,"children":915},{"style":463},[916],{"type":153,"value":917},"socialImage",{"type":148,"tag":423,"props":919,"children":920},{"style":446},[921],{"type":153,"value":517},{"type":148,"tag":423,"props":923,"children":925},{"class":425,"line":924},13,[926,930],{"type":148,"tag":423,"props":927,"children":928},{"style":446},[929],{"type":153,"value":471},{"type":148,"tag":423,"props":931,"children":932},{"style":463},[933],{"type":153,"value":572},{"type":148,"tag":423,"props":935,"children":937},{"class":425,"line":936},14,[938],{"type":148,"tag":423,"props":939,"children":940},{"emptyLinePlaceholder":81},[941],{"type":153,"value":581},{"type":148,"tag":423,"props":943,"children":945},{"class":425,"line":944},15,[946,951,955],{"type":148,"tag":423,"props":947,"children":948},{"style":485},[949],{"type":153,"value":950},"useHead",{"type":148,"tag":423,"props":952,"children":953},{"style":463},[954],{"type":153,"value":594},{"type":148,"tag":423,"props":956,"children":957},{"style":446},[958],{"type":153,"value":599},{"type":148,"tag":423,"props":960,"children":962},{"class":425,"line":961},16,[963,968,972,977,982,987,991,996,1002,1007,1012,1017,1021,1026,1031,1036,1041,1045,1049,1054,1059,1064],{"type":148,"tag":423,"props":964,"children":965},{"style":452},[966],{"type":153,"value":967},"  link",{"type":148,"tag":423,"props":969,"children":970},{"style":446},[971],{"type":153,"value":460},{"type":148,"tag":423,"props":973,"children":974},{"style":463},[975],{"type":153,"value":976}," [",{"type":148,"tag":423,"props":978,"children":979},{"style":446},[980],{"type":153,"value":981},"{",{"type":148,"tag":423,"props":983,"children":984},{"style":452},[985],{"type":153,"value":986}," rel",{"type":148,"tag":423,"props":988,"children":989},{"style":446},[990],{"type":153,"value":460},{"type":148,"tag":423,"props":992,"children":993},{"style":446},[994],{"type":153,"value":995}," '",{"type":148,"tag":423,"props":997,"children":999},{"style":998},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1000],{"type":153,"value":1001},"canonical",{"type":148,"tag":423,"props":1003,"children":1004},{"style":446},[1005],{"type":153,"value":1006},"'",{"type":148,"tag":423,"props":1008,"children":1009},{"style":446},[1010],{"type":153,"value":1011},",",{"type":148,"tag":423,"props":1013,"children":1014},{"style":452},[1015],{"type":153,"value":1016}," href",{"type":148,"tag":423,"props":1018,"children":1019},{"style":446},[1020],{"type":153,"value":460},{"type":148,"tag":423,"props":1022,"children":1023},{"style":446},[1024],{"type":153,"value":1025}," `",{"type":148,"tag":423,"props":1027,"children":1028},{"style":998},[1029],{"type":153,"value":1030},"https:\u002F\u002Fexample.com",{"type":148,"tag":423,"props":1032,"children":1033},{"style":446},[1034],{"type":153,"value":1035},"${",{"type":148,"tag":423,"props":1037,"children":1038},{"style":463},[1039],{"type":153,"value":1040},"route",{"type":148,"tag":423,"props":1042,"children":1043},{"style":446},[1044],{"type":153,"value":507},{"type":148,"tag":423,"props":1046,"children":1047},{"style":463},[1048],{"type":153,"value":512},{"type":148,"tag":423,"props":1050,"children":1051},{"style":446},[1052],{"type":153,"value":1053},"}`",{"type":148,"tag":423,"props":1055,"children":1056},{"style":446},[1057],{"type":153,"value":1058}," }",{"type":148,"tag":423,"props":1060,"children":1061},{"style":463},[1062],{"type":153,"value":1063},"]",{"type":148,"tag":423,"props":1065,"children":1066},{"style":446},[1067],{"type":153,"value":517},{"type":148,"tag":423,"props":1069,"children":1071},{"class":425,"line":1070},17,[1072,1076],{"type":148,"tag":423,"props":1073,"children":1074},{"style":446},[1075],{"type":153,"value":471},{"type":148,"tag":423,"props":1077,"children":1078},{"style":463},[1079],{"type":153,"value":572},{"type":148,"tag":149,"props":1081,"children":1082},{},[1083,1085,1090,1092,1097,1099,1104,1106,1111],{"type":153,"value":1084},"Two things we have learned to insist on here. First, model the SEO fields in the CMS as first-class content, a dedicated ",{"type":148,"tag":356,"props":1086,"children":1088},{"className":1087},[],[1089],{"type":153,"value":18},{"type":153,"value":1091}," group with ",{"type":148,"tag":356,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":153,"value":679},{"type":153,"value":1098},", ",{"type":148,"tag":356,"props":1100,"children":1102},{"className":1101},[],[1103],{"type":153,"value":733},{"type":153,"value":1105},", and ",{"type":148,"tag":356,"props":1107,"children":1109},{"className":1108},[],[1110],{"type":153,"value":917},{"type":153,"value":1112},", so authors control them without touching code. Both Storyblok and Contentful make this easy, and skipping it means every metadata change is a deploy.",{"type":148,"tag":149,"props":1114,"children":1115},{},[1116],{"type":153,"value":1117},"Second, canonicals are where headless teams quietly bleed ranking signal. Because your front end may serve the same content at more than one path (trailing slash or not, uppercase or lowercase, query parameters from campaign tags), you have to pick one canonical form and emit it consistently. Derive the canonical from the CMS content's real slug, not from the request URL, so a campaign-tagged or mixed-case request still points back at the clean URL. This is the single most common canonical bug we find on headless audits.",{"type":148,"tag":156,"props":1119,"children":1121},{"id":1120},"who-owns-redirects-and-url-structure-without-a-monolith",[1122],{"type":153,"value":1123},"Who owns redirects and URL structure without a monolith?",{"type":148,"tag":149,"props":1125,"children":1126},{},[1127],{"type":153,"value":1128},"This catches teams migrating off a traditional CMS. In a monolith, the platform owned redirects: you edited them in an admin panel and the app enforced them. Headless has no such owner by default, so redirects become your responsibility and, if you forget, every changed URL becomes a 404 and every old backlink dies.",{"type":148,"tag":149,"props":1130,"children":1131},{},[1132,1134,1140,1141,1147,1148,1154],{"type":153,"value":1133},"You need two things. A place to store redirects, and a layer that enforces them before the page renders. We model redirects as CMS content (a simple ",{"type":148,"tag":356,"props":1135,"children":1137},{"className":1136},[],[1138],{"type":153,"value":1139},"from",{"type":153,"value":1098},{"type":148,"tag":356,"props":1142,"children":1144},{"className":1143},[],[1145],{"type":153,"value":1146},"to",{"type":153,"value":1098},{"type":148,"tag":356,"props":1149,"children":1151},{"className":1150},[],[1152],{"type":153,"value":1153},"statusCode",{"type":153,"value":1155}," type) so non-engineers can add them, then enforce them in the front end's routing layer. In Nuxt that is server middleware:",{"type":148,"tag":412,"props":1157,"children":1159},{"className":414,"code":1158,"language":416,"meta":417,"style":417},"\u002F\u002F server\u002Fmiddleware\u002Fredirects.ts\nexport default defineEventHandler(async (event) => {\n  const rule = await lookupRedirect(getRequestURL(event).pathname)\n  if (rule) {\n    return sendRedirect(event, rule.to, rule.statusCode ?? 301)\n  }\n})\n",[1160],{"type":148,"tag":356,"props":1161,"children":1162},{"__ignoreMap":417},[1163,1171,1223,1283,1309,1377,1385],{"type":148,"tag":423,"props":1164,"children":1165},{"class":425,"line":426},[1166],{"type":148,"tag":423,"props":1167,"children":1168},{"style":430},[1169],{"type":153,"value":1170},"\u002F\u002F server\u002Fmiddleware\u002Fredirects.ts\n",{"type":148,"tag":423,"props":1172,"children":1173},{"class":425,"line":436},[1174,1179,1184,1189,1193,1198,1203,1209,1214,1218],{"type":148,"tag":423,"props":1175,"children":1176},{"style":479},[1177],{"type":153,"value":1178},"export",{"type":148,"tag":423,"props":1180,"children":1181},{"style":479},[1182],{"type":153,"value":1183}," default",{"type":148,"tag":423,"props":1185,"children":1186},{"style":485},[1187],{"type":153,"value":1188}," defineEventHandler",{"type":148,"tag":423,"props":1190,"children":1191},{"style":463},[1192],{"type":153,"value":594},{"type":148,"tag":423,"props":1194,"children":1195},{"style":440},[1196],{"type":153,"value":1197},"async",{"type":148,"tag":423,"props":1199,"children":1200},{"style":446},[1201],{"type":153,"value":1202}," (",{"type":148,"tag":423,"props":1204,"children":1206},{"style":1205},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[1207],{"type":153,"value":1208},"event",{"type":148,"tag":423,"props":1210,"children":1211},{"style":446},[1212],{"type":153,"value":1213},")",{"type":148,"tag":423,"props":1215,"children":1216},{"style":440},[1217],{"type":153,"value":531},{"type":148,"tag":423,"props":1219,"children":1220},{"style":446},[1221],{"type":153,"value":1222}," {\n",{"type":148,"tag":423,"props":1224,"children":1225},{"class":425,"line":496},[1226,1231,1236,1240,1244,1249,1253,1258,1262,1266,1270,1274,1279],{"type":148,"tag":423,"props":1227,"children":1228},{"style":440},[1229],{"type":153,"value":1230},"  const",{"type":148,"tag":423,"props":1232,"children":1233},{"style":463},[1234],{"type":153,"value":1235}," rule",{"type":148,"tag":423,"props":1237,"children":1238},{"style":446},[1239],{"type":153,"value":476},{"type":148,"tag":423,"props":1241,"children":1242},{"style":479},[1243],{"type":153,"value":482},{"type":148,"tag":423,"props":1245,"children":1246},{"style":485},[1247],{"type":153,"value":1248}," lookupRedirect",{"type":148,"tag":423,"props":1250,"children":1251},{"style":452},[1252],{"type":153,"value":594},{"type":148,"tag":423,"props":1254,"children":1255},{"style":485},[1256],{"type":153,"value":1257},"getRequestURL",{"type":148,"tag":423,"props":1259,"children":1260},{"style":452},[1261],{"type":153,"value":594},{"type":148,"tag":423,"props":1263,"children":1264},{"style":463},[1265],{"type":153,"value":1208},{"type":148,"tag":423,"props":1267,"children":1268},{"style":452},[1269],{"type":153,"value":1213},{"type":148,"tag":423,"props":1271,"children":1272},{"style":446},[1273],{"type":153,"value":507},{"type":148,"tag":423,"props":1275,"children":1276},{"style":463},[1277],{"type":153,"value":1278},"pathname",{"type":148,"tag":423,"props":1280,"children":1281},{"style":452},[1282],{"type":153,"value":572},{"type":148,"tag":423,"props":1284,"children":1285},{"class":425,"line":520},[1286,1291,1295,1300,1305],{"type":148,"tag":423,"props":1287,"children":1288},{"style":479},[1289],{"type":153,"value":1290},"  if",{"type":148,"tag":423,"props":1292,"children":1293},{"style":452},[1294],{"type":153,"value":1202},{"type":148,"tag":423,"props":1296,"children":1297},{"style":463},[1298],{"type":153,"value":1299},"rule",{"type":148,"tag":423,"props":1301,"children":1302},{"style":452},[1303],{"type":153,"value":1304},") ",{"type":148,"tag":423,"props":1306,"children":1307},{"style":446},[1308],{"type":153,"value":599},{"type":148,"tag":423,"props":1310,"children":1311},{"class":425,"line":566},[1312,1317,1322,1326,1330,1334,1338,1342,1346,1350,1354,1358,1362,1367,1373],{"type":148,"tag":423,"props":1313,"children":1314},{"style":479},[1315],{"type":153,"value":1316},"    return",{"type":148,"tag":423,"props":1318,"children":1319},{"style":485},[1320],{"type":153,"value":1321}," sendRedirect",{"type":148,"tag":423,"props":1323,"children":1324},{"style":452},[1325],{"type":153,"value":594},{"type":148,"tag":423,"props":1327,"children":1328},{"style":463},[1329],{"type":153,"value":1208},{"type":148,"tag":423,"props":1331,"children":1332},{"style":446},[1333],{"type":153,"value":1011},{"type":148,"tag":423,"props":1335,"children":1336},{"style":463},[1337],{"type":153,"value":1235},{"type":148,"tag":423,"props":1339,"children":1340},{"style":446},[1341],{"type":153,"value":507},{"type":148,"tag":423,"props":1343,"children":1344},{"style":463},[1345],{"type":153,"value":1146},{"type":148,"tag":423,"props":1347,"children":1348},{"style":446},[1349],{"type":153,"value":1011},{"type":148,"tag":423,"props":1351,"children":1352},{"style":463},[1353],{"type":153,"value":1235},{"type":148,"tag":423,"props":1355,"children":1356},{"style":446},[1357],{"type":153,"value":507},{"type":148,"tag":423,"props":1359,"children":1360},{"style":463},[1361],{"type":153,"value":1153},{"type":148,"tag":423,"props":1363,"children":1364},{"style":446},[1365],{"type":153,"value":1366}," ??",{"type":148,"tag":423,"props":1368,"children":1370},{"style":1369},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[1371],{"type":153,"value":1372}," 301",{"type":148,"tag":423,"props":1374,"children":1375},{"style":452},[1376],{"type":153,"value":572},{"type":148,"tag":423,"props":1378,"children":1379},{"class":425,"line":575},[1380],{"type":148,"tag":423,"props":1381,"children":1382},{"style":446},[1383],{"type":153,"value":1384},"  }\n",{"type":148,"tag":423,"props":1386,"children":1387},{"class":425,"line":584},[1388,1392],{"type":148,"tag":423,"props":1389,"children":1390},{"style":446},[1391],{"type":153,"value":471},{"type":148,"tag":423,"props":1393,"children":1394},{"style":463},[1395],{"type":153,"value":572},{"type":148,"tag":149,"props":1397,"children":1398},{},[1399,1401,1407,1409,1415],{"type":153,"value":1400},"On URL structure, headless gives you full control, which is a gift and a trap. The gift: clean, flat, keyword-relevant paths with no ",{"type":148,"tag":356,"props":1402,"children":1404},{"className":1403},[],[1405],{"type":153,"value":1406},"\u002Findex.php?id=42",{"type":153,"value":1408}," legacy. The trap: because routing is code, it is easy to let the front-end folder structure dictate URLs rather than deriving them from the content model. Derive the path from a ",{"type":148,"tag":356,"props":1410,"children":1412},{"className":1411},[],[1413],{"type":153,"value":1414},"fullSlug",{"type":153,"value":1416}," field on the content, keep it stable, and treat any change to it as a redirect event. During a migration, export every existing indexed URL (Google Search Console and your server logs are the sources of truth) and confirm each one either still resolves or 301s. That reconciliation is the difference between a migration that holds its rankings and one that craters them.",{"type":148,"tag":156,"props":1418,"children":1420},{"id":1419},"how-do-you-add-structured-data-json-ld-from-the-front-end",[1421],{"type":153,"value":1422},"How do you add structured data (JSON-LD) from the front end?",{"type":148,"tag":149,"props":1424,"children":1425},{},[1426],{"type":153,"value":1427},"The same way you add everything else in headless: the front end generates it, server-side, from CMS content. No plugin injects schema behind your back, which is an advantage: you control it precisely and can drive it straight from your structured content.",{"type":148,"tag":149,"props":1429,"children":1430},{},[1431,1433,1439],{"type":153,"value":1432},"We use the ",{"type":148,"tag":356,"props":1434,"children":1436},{"className":1435},[],[1437],{"type":153,"value":1438},"nuxt-schema-org",{"type":153,"value":1440}," approach: define the graph declaratively and let it render JSON-LD into the server response. For an article, that means mapping CMS fields onto the schema:",{"type":148,"tag":412,"props":1442,"children":1444},{"className":414,"code":1443,"language":416,"meta":417,"style":417},"useSchemaOrg([\n  defineArticle({\n    headline: page.value.title,\n    description: page.value.seo?.description,\n    image: page.value.coverImage,\n    datePublished: page.value.publishedAt,\n    dateModified: page.value.updatedAt,\n    author: { name: page.value.author?.name },\n  }),\n])\n",[1445],{"type":148,"tag":356,"props":1446,"children":1447},{"__ignoreMap":417},[1448,1461,1477,1513,1557,1594,1631,1668,1728,1744],{"type":148,"tag":423,"props":1449,"children":1450},{"class":425,"line":426},[1451,1456],{"type":148,"tag":423,"props":1452,"children":1453},{"style":485},[1454],{"type":153,"value":1455},"useSchemaOrg",{"type":148,"tag":423,"props":1457,"children":1458},{"style":463},[1459],{"type":153,"value":1460},"([\n",{"type":148,"tag":423,"props":1462,"children":1463},{"class":425,"line":436},[1464,1469,1473],{"type":148,"tag":423,"props":1465,"children":1466},{"style":485},[1467],{"type":153,"value":1468},"  defineArticle",{"type":148,"tag":423,"props":1470,"children":1471},{"style":463},[1472],{"type":153,"value":594},{"type":148,"tag":423,"props":1474,"children":1475},{"style":446},[1476],{"type":153,"value":599},{"type":148,"tag":423,"props":1478,"children":1479},{"class":425,"line":496},[1480,1485,1489,1493,1497,1501,1505,1509],{"type":148,"tag":423,"props":1481,"children":1482},{"style":452},[1483],{"type":153,"value":1484},"    headline",{"type":148,"tag":423,"props":1486,"children":1487},{"style":446},[1488],{"type":153,"value":460},{"type":148,"tag":423,"props":1490,"children":1491},{"style":463},[1492],{"type":153,"value":626},{"type":148,"tag":423,"props":1494,"children":1495},{"style":446},[1496],{"type":153,"value":507},{"type":148,"tag":423,"props":1498,"children":1499},{"style":463},[1500],{"type":153,"value":635},{"type":148,"tag":423,"props":1502,"children":1503},{"style":446},[1504],{"type":153,"value":507},{"type":148,"tag":423,"props":1506,"children":1507},{"style":463},[1508],{"type":153,"value":679},{"type":148,"tag":423,"props":1510,"children":1511},{"style":446},[1512],{"type":153,"value":517},{"type":148,"tag":423,"props":1514,"children":1515},{"class":425,"line":520},[1516,1521,1525,1529,1533,1537,1541,1545,1549,1553],{"type":148,"tag":423,"props":1517,"children":1518},{"style":452},[1519],{"type":153,"value":1520},"    description",{"type":148,"tag":423,"props":1522,"children":1523},{"style":446},[1524],{"type":153,"value":460},{"type":148,"tag":423,"props":1526,"children":1527},{"style":463},[1528],{"type":153,"value":626},{"type":148,"tag":423,"props":1530,"children":1531},{"style":446},[1532],{"type":153,"value":507},{"type":148,"tag":423,"props":1534,"children":1535},{"style":463},[1536],{"type":153,"value":635},{"type":148,"tag":423,"props":1538,"children":1539},{"style":446},[1540],{"type":153,"value":507},{"type":148,"tag":423,"props":1542,"children":1543},{"style":463},[1544],{"type":153,"value":18},{"type":148,"tag":423,"props":1546,"children":1547},{"style":446},[1548],{"type":153,"value":640},{"type":148,"tag":423,"props":1550,"children":1551},{"style":463},[1552],{"type":153,"value":733},{"type":148,"tag":423,"props":1554,"children":1555},{"style":446},[1556],{"type":153,"value":517},{"type":148,"tag":423,"props":1558,"children":1559},{"class":425,"line":566},[1560,1565,1569,1573,1577,1581,1585,1590],{"type":148,"tag":423,"props":1561,"children":1562},{"style":452},[1563],{"type":153,"value":1564},"    image",{"type":148,"tag":423,"props":1566,"children":1567},{"style":446},[1568],{"type":153,"value":460},{"type":148,"tag":423,"props":1570,"children":1571},{"style":463},[1572],{"type":153,"value":626},{"type":148,"tag":423,"props":1574,"children":1575},{"style":446},[1576],{"type":153,"value":507},{"type":148,"tag":423,"props":1578,"children":1579},{"style":463},[1580],{"type":153,"value":635},{"type":148,"tag":423,"props":1582,"children":1583},{"style":446},[1584],{"type":153,"value":507},{"type":148,"tag":423,"props":1586,"children":1587},{"style":463},[1588],{"type":153,"value":1589},"coverImage",{"type":148,"tag":423,"props":1591,"children":1592},{"style":446},[1593],{"type":153,"value":517},{"type":148,"tag":423,"props":1595,"children":1596},{"class":425,"line":575},[1597,1602,1606,1610,1614,1618,1622,1627],{"type":148,"tag":423,"props":1598,"children":1599},{"style":452},[1600],{"type":153,"value":1601},"    datePublished",{"type":148,"tag":423,"props":1603,"children":1604},{"style":446},[1605],{"type":153,"value":460},{"type":148,"tag":423,"props":1607,"children":1608},{"style":463},[1609],{"type":153,"value":626},{"type":148,"tag":423,"props":1611,"children":1612},{"style":446},[1613],{"type":153,"value":507},{"type":148,"tag":423,"props":1615,"children":1616},{"style":463},[1617],{"type":153,"value":635},{"type":148,"tag":423,"props":1619,"children":1620},{"style":446},[1621],{"type":153,"value":507},{"type":148,"tag":423,"props":1623,"children":1624},{"style":463},[1625],{"type":153,"value":1626},"publishedAt",{"type":148,"tag":423,"props":1628,"children":1629},{"style":446},[1630],{"type":153,"value":517},{"type":148,"tag":423,"props":1632,"children":1633},{"class":425,"line":584},[1634,1639,1643,1647,1651,1655,1659,1664],{"type":148,"tag":423,"props":1635,"children":1636},{"style":452},[1637],{"type":153,"value":1638},"    dateModified",{"type":148,"tag":423,"props":1640,"children":1641},{"style":446},[1642],{"type":153,"value":460},{"type":148,"tag":423,"props":1644,"children":1645},{"style":463},[1646],{"type":153,"value":626},{"type":148,"tag":423,"props":1648,"children":1649},{"style":446},[1650],{"type":153,"value":507},{"type":148,"tag":423,"props":1652,"children":1653},{"style":463},[1654],{"type":153,"value":635},{"type":148,"tag":423,"props":1656,"children":1657},{"style":446},[1658],{"type":153,"value":507},{"type":148,"tag":423,"props":1660,"children":1661},{"style":463},[1662],{"type":153,"value":1663},"updatedAt",{"type":148,"tag":423,"props":1665,"children":1666},{"style":446},[1667],{"type":153,"value":517},{"type":148,"tag":423,"props":1669,"children":1670},{"class":425,"line":602},[1671,1676,1680,1684,1689,1693,1697,1701,1705,1709,1714,1718,1723],{"type":148,"tag":423,"props":1672,"children":1673},{"style":452},[1674],{"type":153,"value":1675},"    author",{"type":148,"tag":423,"props":1677,"children":1678},{"style":446},[1679],{"type":153,"value":460},{"type":148,"tag":423,"props":1681,"children":1682},{"style":446},[1683],{"type":153,"value":449},{"type":148,"tag":423,"props":1685,"children":1686},{"style":452},[1687],{"type":153,"value":1688}," name",{"type":148,"tag":423,"props":1690,"children":1691},{"style":446},[1692],{"type":153,"value":460},{"type":148,"tag":423,"props":1694,"children":1695},{"style":463},[1696],{"type":153,"value":626},{"type":148,"tag":423,"props":1698,"children":1699},{"style":446},[1700],{"type":153,"value":507},{"type":148,"tag":423,"props":1702,"children":1703},{"style":463},[1704],{"type":153,"value":635},{"type":148,"tag":423,"props":1706,"children":1707},{"style":446},[1708],{"type":153,"value":507},{"type":148,"tag":423,"props":1710,"children":1711},{"style":463},[1712],{"type":153,"value":1713},"author",{"type":148,"tag":423,"props":1715,"children":1716},{"style":446},[1717],{"type":153,"value":640},{"type":148,"tag":423,"props":1719,"children":1720},{"style":463},[1721],{"type":153,"value":1722},"name ",{"type":148,"tag":423,"props":1724,"children":1725},{"style":446},[1726],{"type":153,"value":1727},"},\n",{"type":148,"tag":423,"props":1729,"children":1730},{"class":425,"line":686},[1731,1736,1740],{"type":148,"tag":423,"props":1732,"children":1733},{"style":446},[1734],{"type":153,"value":1735},"  }",{"type":148,"tag":423,"props":1737,"children":1738},{"style":463},[1739],{"type":153,"value":1213},{"type":148,"tag":423,"props":1741,"children":1742},{"style":446},[1743],{"type":153,"value":517},{"type":148,"tag":423,"props":1745,"children":1746},{"class":425,"line":740},[1747],{"type":148,"tag":423,"props":1748,"children":1749},{"style":463},[1750],{"type":153,"value":1751},"])\n",{"type":148,"tag":149,"props":1753,"children":1754},{},[1755,1757,1763,1764,1770,1771,1777,1778,1784,1786,1792],{"type":153,"value":1756},"Because JSON-LD is just structured content describing structured content, headless is a natural fit: your CMS already holds the author, the publish date, the FAQ entries. Map those fields to ",{"type":148,"tag":356,"props":1758,"children":1760},{"className":1759},[],[1761],{"type":153,"value":1762},"Article",{"type":153,"value":1098},{"type":148,"tag":356,"props":1765,"children":1767},{"className":1766},[],[1768],{"type":153,"value":1769},"BreadcrumbList",{"type":153,"value":1098},{"type":148,"tag":356,"props":1772,"children":1774},{"className":1773},[],[1775],{"type":153,"value":1776},"Organization",{"type":153,"value":1098},{"type":148,"tag":356,"props":1779,"children":1781},{"className":1780},[],[1782],{"type":153,"value":1783},"FAQPage",{"type":153,"value":1785},", or ",{"type":148,"tag":356,"props":1787,"children":1789},{"className":1788},[],[1790],{"type":153,"value":1791},"Product",{"type":153,"value":1793}," schema and you get rich-result eligibility without hand-writing brittle JSON in a template. The rule that keeps you out of trouble: schema must describe content that is actually visible on the page. FAQ schema pointing at questions a user cannot see is a manual-action risk, not a shortcut.",{"type":148,"tag":156,"props":1795,"children":1797},{"id":1796},"how-do-you-generate-sitemaps-and-robotstxt-from-a-headless-source",[1798],{"type":153,"value":1799},"How do you generate sitemaps and robots.txt from a headless source?",{"type":148,"tag":149,"props":1801,"children":1802},{},[1803,1805,1811],{"type":153,"value":1804},"Dynamically, from the CMS, because the CMS is the only thing that knows the full set of URLs. A hand-maintained ",{"type":148,"tag":356,"props":1806,"children":1808},{"className":1807},[],[1809],{"type":153,"value":1810},"sitemap.xml",{"type":153,"value":1812}," in a headless project is stale the moment an author publishes.",{"type":148,"tag":149,"props":1814,"children":1815},{},[1816,1818,1824,1826,1832],{"type":153,"value":1817},"The pattern is a server route that queries the CMS for every publishable content item and emits the XML. In Nuxt the ",{"type":148,"tag":356,"props":1819,"children":1821},{"className":1820},[],[1822],{"type":153,"value":1823},"@nuxtjs\u002Fsitemap",{"type":153,"value":1825}," module does this well: point it at a source that lists your URLs and it builds the sitemap at request time (or build time for SSG), including ",{"type":148,"tag":356,"props":1827,"children":1829},{"className":1828},[],[1830],{"type":153,"value":1831},"lastmod",{"type":153,"value":1833}," from the content's updated date.",{"type":148,"tag":412,"props":1835,"children":1837},{"className":414,"code":1836,"language":416,"meta":417,"style":417},"\u002F\u002F server\u002Fapi\u002F__sitemap__\u002Furls.ts\nexport default defineSitemapEventHandler(async () => {\n  const items = await $cms.listPublished()\n  return items.map((item) => ({\n    loc: item.fullSlug,\n    lastmod: item.updatedAt,\n  }))\n})\n",[1838],{"type":148,"tag":356,"props":1839,"children":1840},{"__ignoreMap":417},[1841,1849,1885,1923,1973,2002,2030,2042],{"type":148,"tag":423,"props":1842,"children":1843},{"class":425,"line":426},[1844],{"type":148,"tag":423,"props":1845,"children":1846},{"style":430},[1847],{"type":153,"value":1848},"\u002F\u002F server\u002Fapi\u002F__sitemap__\u002Furls.ts\n",{"type":148,"tag":423,"props":1850,"children":1851},{"class":425,"line":436},[1852,1856,1860,1865,1869,1873,1877,1881],{"type":148,"tag":423,"props":1853,"children":1854},{"style":479},[1855],{"type":153,"value":1178},{"type":148,"tag":423,"props":1857,"children":1858},{"style":479},[1859],{"type":153,"value":1183},{"type":148,"tag":423,"props":1861,"children":1862},{"style":485},[1863],{"type":153,"value":1864}," defineSitemapEventHandler",{"type":148,"tag":423,"props":1866,"children":1867},{"style":463},[1868],{"type":153,"value":594},{"type":148,"tag":423,"props":1870,"children":1871},{"style":440},[1872],{"type":153,"value":1197},{"type":148,"tag":423,"props":1874,"children":1875},{"style":446},[1876],{"type":153,"value":617},{"type":148,"tag":423,"props":1878,"children":1879},{"style":440},[1880],{"type":153,"value":531},{"type":148,"tag":423,"props":1882,"children":1883},{"style":446},[1884],{"type":153,"value":1222},{"type":148,"tag":423,"props":1886,"children":1887},{"class":425,"line":496},[1888,1892,1897,1901,1905,1909,1913,1918],{"type":148,"tag":423,"props":1889,"children":1890},{"style":440},[1891],{"type":153,"value":1230},{"type":148,"tag":423,"props":1893,"children":1894},{"style":463},[1895],{"type":153,"value":1896}," items",{"type":148,"tag":423,"props":1898,"children":1899},{"style":446},[1900],{"type":153,"value":476},{"type":148,"tag":423,"props":1902,"children":1903},{"style":479},[1904],{"type":153,"value":482},{"type":148,"tag":423,"props":1906,"children":1907},{"style":463},[1908],{"type":153,"value":536},{"type":148,"tag":423,"props":1910,"children":1911},{"style":446},[1912],{"type":153,"value":507},{"type":148,"tag":423,"props":1914,"children":1915},{"style":485},[1916],{"type":153,"value":1917},"listPublished",{"type":148,"tag":423,"props":1919,"children":1920},{"style":452},[1921],{"type":153,"value":1922},"()\n",{"type":148,"tag":423,"props":1924,"children":1925},{"class":425,"line":520},[1926,1931,1935,1939,1944,1948,1952,1957,1961,1965,1969],{"type":148,"tag":423,"props":1927,"children":1928},{"style":479},[1929],{"type":153,"value":1930},"  return",{"type":148,"tag":423,"props":1932,"children":1933},{"style":463},[1934],{"type":153,"value":1896},{"type":148,"tag":423,"props":1936,"children":1937},{"style":446},[1938],{"type":153,"value":507},{"type":148,"tag":423,"props":1940,"children":1941},{"style":485},[1942],{"type":153,"value":1943},"map",{"type":148,"tag":423,"props":1945,"children":1946},{"style":452},[1947],{"type":153,"value":594},{"type":148,"tag":423,"props":1949,"children":1950},{"style":446},[1951],{"type":153,"value":594},{"type":148,"tag":423,"props":1953,"children":1954},{"style":1205},[1955],{"type":153,"value":1956},"item",{"type":148,"tag":423,"props":1958,"children":1959},{"style":446},[1960],{"type":153,"value":1213},{"type":148,"tag":423,"props":1962,"children":1963},{"style":440},[1964],{"type":153,"value":531},{"type":148,"tag":423,"props":1966,"children":1967},{"style":452},[1968],{"type":153,"value":1202},{"type":148,"tag":423,"props":1970,"children":1971},{"style":446},[1972],{"type":153,"value":599},{"type":148,"tag":423,"props":1974,"children":1975},{"class":425,"line":566},[1976,1981,1985,1990,1994,1998],{"type":148,"tag":423,"props":1977,"children":1978},{"style":452},[1979],{"type":153,"value":1980},"    loc",{"type":148,"tag":423,"props":1982,"children":1983},{"style":446},[1984],{"type":153,"value":460},{"type":148,"tag":423,"props":1986,"children":1987},{"style":463},[1988],{"type":153,"value":1989}," item",{"type":148,"tag":423,"props":1991,"children":1992},{"style":446},[1993],{"type":153,"value":507},{"type":148,"tag":423,"props":1995,"children":1996},{"style":463},[1997],{"type":153,"value":1414},{"type":148,"tag":423,"props":1999,"children":2000},{"style":446},[2001],{"type":153,"value":517},{"type":148,"tag":423,"props":2003,"children":2004},{"class":425,"line":575},[2005,2010,2014,2018,2022,2026],{"type":148,"tag":423,"props":2006,"children":2007},{"style":452},[2008],{"type":153,"value":2009},"    lastmod",{"type":148,"tag":423,"props":2011,"children":2012},{"style":446},[2013],{"type":153,"value":460},{"type":148,"tag":423,"props":2015,"children":2016},{"style":463},[2017],{"type":153,"value":1989},{"type":148,"tag":423,"props":2019,"children":2020},{"style":446},[2021],{"type":153,"value":507},{"type":148,"tag":423,"props":2023,"children":2024},{"style":463},[2025],{"type":153,"value":1663},{"type":148,"tag":423,"props":2027,"children":2028},{"style":446},[2029],{"type":153,"value":517},{"type":148,"tag":423,"props":2031,"children":2032},{"class":425,"line":584},[2033,2037],{"type":148,"tag":423,"props":2034,"children":2035},{"style":446},[2036],{"type":153,"value":1735},{"type":148,"tag":423,"props":2038,"children":2039},{"style":452},[2040],{"type":153,"value":2041},"))\n",{"type":148,"tag":423,"props":2043,"children":2044},{"class":425,"line":602},[2045,2049],{"type":148,"tag":423,"props":2046,"children":2047},{"style":446},[2048],{"type":153,"value":471},{"type":148,"tag":423,"props":2050,"children":2051},{"style":463},[2052],{"type":153,"value":572},{"type":148,"tag":149,"props":2054,"children":2055},{},[2056,2062,2064,2070,2072,2078],{"type":148,"tag":356,"props":2057,"children":2059},{"className":2058},[],[2060],{"type":153,"value":2061},"robots.txt",{"type":153,"value":2063}," deserves specific care in headless because of the failure mode in the next section. Keep it simple: allow crawling of production and point at the sitemap. Then make absolutely sure your preview and staging environments serve a ",{"type":148,"tag":356,"props":2065,"children":2067},{"className":2066},[],[2068],{"type":153,"value":2069},"Disallow: \u002F",{"type":153,"value":2071}," robots file plus a ",{"type":148,"tag":356,"props":2073,"children":2075},{"className":2074},[],[2076],{"type":153,"value":2077},"noindex",{"type":153,"value":2079}," header. The environment split is the thing that goes wrong, so make it explicit rather than assumed.",{"type":148,"tag":156,"props":2081,"children":2083},{"id":2082},"what-core-web-vitals-wins-does-headless-make-possible",[2084],{"type":153,"value":2085},"What Core Web Vitals wins does headless make possible?",{"type":148,"tag":149,"props":2087,"children":2088},{},[2089],{"type":153,"value":2090},"Real ones, and this is where a well-built headless front end pulls ahead of a traditional theme. Because you control the entire output, you can hit Core Web Vitals targets that a plugin-laden monolith struggles to reach.",{"type":148,"tag":2092,"props":2093,"children":2094},"ul",{},[2095,2114,2124,2134],{"type":148,"tag":2096,"props":2097,"children":2098},"li",{},[2099,2104,2106,2112],{"type":148,"tag":212,"props":2100,"children":2101},{},[2102],{"type":153,"value":2103},"Largest Contentful Paint (LCP).",{"type":153,"value":2105}," SSR or SSG means the main content is in the initial HTML, so there is no round trip to an API before the hero renders. Pair that with proper image handling (responsive ",{"type":148,"tag":356,"props":2107,"children":2109},{"className":2108},[],[2110],{"type":153,"value":2111},"srcset",{"type":153,"value":2113},", modern formats, and priority hints on the LCP image) and LCP gets easy. On our Nuxt builds this is where the biggest gains show up.",{"type":148,"tag":2096,"props":2115,"children":2116},{},[2117,2122],{"type":148,"tag":212,"props":2118,"children":2119},{},[2120],{"type":153,"value":2121},"Cumulative Layout Shift (CLS).",{"type":153,"value":2123}," You control the markup, so you can reserve space for images and embeds with explicit dimensions and avoid the layout jank that theme-injected content causes.",{"type":148,"tag":2096,"props":2125,"children":2126},{},[2127,2132],{"type":148,"tag":212,"props":2128,"children":2129},{},[2130],{"type":153,"value":2131},"Interaction to Next Paint (INP).",{"type":153,"value":2133}," A headless front end ships only the JavaScript you write, not a stack of plugin scripts. Less script means faster interaction. The discipline is to keep hydration lean and defer non-critical third-party scripts.",{"type":148,"tag":2096,"props":2135,"children":2136},{},[2137,2142],{"type":148,"tag":212,"props":2138,"children":2139},{},[2140],{"type":153,"value":2141},"Time to First Byte (TTFB).",{"type":153,"value":2143}," Static or cached SSR served from an edge network is fast at the origin. The lever most teams miss is caching: put a CDN in front of SSR so the crawler and most users hit cache, not a cold render.",{"type":148,"tag":149,"props":2145,"children":2146},{},[2147],{"type":153,"value":2148},"None of this is automatic. Headless gives you the ceiling, and disciplined front-end engineering is how you reach it. A headless site with unoptimized images and a bloated bundle can score worse than a decent traditional theme.",{"type":148,"tag":156,"props":2150,"children":2152},{"id":2151},"why-does-preview-and-draft-content-leak-to-crawlers-and-how-do-you-stop-it",[2153],{"type":153,"value":2154},"Why does preview and draft content leak to crawlers, and how do you stop it?",{"type":148,"tag":149,"props":2156,"children":2157},{},[2158],{"type":153,"value":2159},"This is the classic headless SEO failure, and we see it more than any other. In a traditional CMS, drafts live behind the admin login and are never served publicly. In headless, your preview environment is often a real, publicly-reachable URL that renders draft content straight from the CMS with drafts enabled. If a crawler finds that URL, it indexes your unpublished content, your duplicate staging pages, and sometimes content you never intended to publish at all.",{"type":148,"tag":149,"props":2161,"children":2162},{},[2163,2165,2170,2172,2177],{"type":153,"value":2164},"It leaks through a few doors. A preview deploy on a guessable subdomain with no ",{"type":148,"tag":356,"props":2166,"children":2168},{"className":2167},[],[2169],{"type":153,"value":2077},{"type":153,"value":2171},". Draft mode toggled by a query parameter that a crawler stumbles into. A ",{"type":148,"tag":356,"props":2173,"children":2175},{"className":2174},[],[2176],{"type":153,"value":1810},{"type":153,"value":2178}," on staging that lists real URLs. An internal link to a preview URL that gets crawled.",{"type":148,"tag":149,"props":2180,"children":2181},{},[2182],{"type":153,"value":2183},"The fixes are boring and they work.",{"type":148,"tag":2185,"props":2186,"children":2187},"ol",{},[2188,2201,2219,2224,2229],{"type":148,"tag":2096,"props":2189,"children":2190},{},[2191,2193,2199],{"type":153,"value":2192},"Serve ",{"type":148,"tag":356,"props":2194,"children":2196},{"className":2195},[],[2197],{"type":153,"value":2198},"X-Robots-Tag: noindex, nofollow",{"type":153,"value":2200}," on every non-production environment, at the edge or in server middleware, not per-page where it is easy to miss.",{"type":148,"tag":2096,"props":2202,"children":2203},{},[2204,2205,2210,2212,2217],{"type":153,"value":2192},{"type":148,"tag":356,"props":2206,"children":2208},{"className":2207},[],[2209],{"type":153,"value":2069},{"type":153,"value":2211}," in ",{"type":148,"tag":356,"props":2213,"children":2215},{"className":2214},[],[2216],{"type":153,"value":2061},{"type":153,"value":2218}," on preview and staging.",{"type":148,"tag":2096,"props":2220,"children":2221},{},[2222],{"type":153,"value":2223},"Put preview behind authentication (a shared password or an access gate) so it is not publicly fetchable at all. This is the strongest control.",{"type":148,"tag":2096,"props":2225,"children":2226},{},[2227],{"type":153,"value":2228},"Ensure your API's \"include drafts\" flag is driven by an environment variable that is false in production and cannot be flipped by a query parameter.",{"type":148,"tag":2096,"props":2230,"children":2231},{},[2232],{"type":153,"value":2233},"Never let a staging environment emit a public sitemap.",{"type":148,"tag":149,"props":2235,"children":2236},{},[2237,2239,2245],{"type":153,"value":2238},"The draft-leak problem overlaps with headless security more broadly, because the same misconfigurations that expose drafts to Google often expose your CMS API surface too. We cover that surface in ",{"type":148,"tag":172,"props":2240,"children":2242},{"href":2241},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-security",[2243],{"type":153,"value":2244},"headless CMS security",{"type":153,"value":2246},". For SEO specifically, treat \"can a crawler reach a non-production URL\" as a launch-blocking checklist item, not a nice-to-have.",{"type":148,"tag":156,"props":2248,"children":2250},{"id":2249},"headless-cms-seo-best-practices-in-short",[2251],{"type":153,"value":2252},"Headless CMS SEO best practices, in short",{"type":148,"tag":149,"props":2254,"children":2255},{},[2256],{"type":153,"value":2257},"If you take nothing else from this guide, take these.",{"type":148,"tag":2092,"props":2259,"children":2260},{},[2261,2273,2278,2283,2295,2300],{"type":148,"tag":2096,"props":2262,"children":2263},{},[2264,2266,2271],{"type":153,"value":2265},"Render server-side (SSR or SSG\u002FISR) for every page you want to rank. Verify with view-source or ",{"type":148,"tag":356,"props":2267,"children":2269},{"className":2268},[],[2270],{"type":153,"value":361},{"type":153,"value":2272},", not the inspector.",{"type":148,"tag":2096,"props":2274,"children":2275},{},[2276],{"type":153,"value":2277},"Set titles, descriptions, and canonicals server-side from CMS-modeled SEO fields, and derive canonicals from the content slug, not the request URL.",{"type":148,"tag":2096,"props":2279,"children":2280},{},[2281],{"type":153,"value":2282},"Own your redirects deliberately: store them, enforce them before render, and reconcile every old URL during a migration.",{"type":148,"tag":2096,"props":2284,"children":2285},{},[2286,2288,2293],{"type":153,"value":2287},"Generate sitemaps dynamically from the CMS with real ",{"type":148,"tag":356,"props":2289,"children":2291},{"className":2290},[],[2292],{"type":153,"value":1831},{"type":153,"value":2294}," values.",{"type":148,"tag":2096,"props":2296,"children":2297},{},[2298],{"type":153,"value":2299},"Use the ceiling headless gives you on Core Web Vitals, and do the image and bundle work to actually reach it.",{"type":148,"tag":2096,"props":2301,"children":2302},{},[2303],{"type":153,"value":2304},"Lock down preview and staging so drafts never reach a crawler.",{"type":148,"tag":149,"props":2306,"children":2307},{},[2308,2310,2316,2318,2324,2326,2332],{"type":153,"value":2309},"Handled with that discipline, a headless build is one of the best SEO foundations you can have, which is exactly why we build SSR-first front ends for content that has to be found. If you are choosing a stack, migrating off a monolith, or trying to figure out why a headless site is not ranking, that is the work we do. See our ",{"type":148,"tag":172,"props":2311,"children":2313},{"href":2312},"\u002Fservices\u002Fheadless-cms",[2314],{"type":153,"value":2315},"headless CMS agency",{"type":153,"value":2317}," practice, or if you are on the enterprise Digital Experience Platform (DXP) side, our ",{"type":148,"tag":172,"props":2319,"children":2321},{"href":2320},"\u002Fservices\u002Fsitecore",[2322],{"type":153,"value":2323},"Sitecore development and support",{"type":153,"value":2325}," work. If you want a second set of eyes on a specific build, ",{"type":148,"tag":172,"props":2327,"children":2329},{"href":2328},"\u002Fbook",[2330],{"type":153,"value":2331},"book a call",{"type":153,"value":2333}," and we will look at the actual rendered output.",{"type":148,"tag":156,"props":2335,"children":2337},{"id":2336},"read-next",[2338],{"type":153,"value":2339},"Read next",{"type":148,"tag":2092,"props":2341,"children":2342},{},[2343,2353,2363,2374,2385,2396],{"type":148,"tag":2096,"props":2344,"children":2345},{},[2346,2351],{"type":148,"tag":172,"props":2347,"children":2348},{"href":174},[2349],{"type":153,"value":2350},"Headless CMS vs Traditional CMS",{"type":153,"value":2352},": the full architecture tradeoff beyond search.",{"type":148,"tag":2096,"props":2354,"children":2355},{},[2356,2361],{"type":148,"tag":172,"props":2357,"children":2358},{"href":2241},[2359],{"type":153,"value":2360},"Headless CMS Security",{"type":153,"value":2362},": the API-surface and preview-environment risks that overlap with the draft-leak problem here.",{"type":148,"tag":2096,"props":2364,"children":2365},{},[2366,2372],{"type":148,"tag":172,"props":2367,"children":2369},{"href":2368},"\u002Fblog\u002Fenterprise-cms\u002Fenterprise-cms-2026-platform-and-partner",[2370],{"type":153,"value":2371},"Enterprise CMS in 2026: Platform and Partner",{"type":153,"value":2373},": how to choose the platform and who delivers it.",{"type":148,"tag":2096,"props":2375,"children":2376},{},[2377,2383],{"type":148,"tag":172,"props":2378,"children":2380},{"href":2379},"\u002Fblog\u002Fenterprise-cms\u002Fenterprise-headless-cms-explained",[2381],{"type":153,"value":2382},"Enterprise Headless CMS, Explained",{"type":153,"value":2384},": what \"enterprise\" adds on top of a basic headless setup.",{"type":148,"tag":2096,"props":2386,"children":2387},{},[2388,2394],{"type":148,"tag":172,"props":2389,"children":2391},{"href":2390},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-localization",[2392],{"type":153,"value":2393},"Headless CMS Localization",{"type":153,"value":2395},": multi-language and multi-region content without breaking SEO.",{"type":148,"tag":2096,"props":2397,"children":2398},{},[2399,2401,2405,2407,2411],{"type":153,"value":2400},"Work with us: our ",{"type":148,"tag":172,"props":2402,"children":2403},{"href":2312},[2404],{"type":153,"value":2315},{"type":153,"value":2406}," practice, or ",{"type":148,"tag":172,"props":2408,"children":2409},{"href":2320},[2410],{"type":153,"value":2323},{"type":153,"value":507},{"type":148,"tag":2413,"props":2414,"children":2415},"style",{},[2416],{"type":153,"value":2417},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}"]