Frameworks & Hosting8 min read

How to Integrate Nuxt with Your Headless CMS

Connect Nuxt to structured content so pages, server routes, cached builds, and localized experiences update from one content workflow instead of a code deploy.

Published April 29, 2026
01Overview

What is Nuxt?

Nuxt is an open-source framework for building Vue applications with server rendering, static generation, hybrid rendering, API routes, and file-based routing. Teams use it for marketing sites, ecommerce frontends, documentation, portals, and apps that need Vue with production-ready routing, data fetching, and deployment patterns. Its core strength is letting you choose per route whether content is rendered on the server, generated at build time, cached, or fetched in the browser.


02The case for integration

Why integrate Nuxt with a headless CMS?

Nuxt is great at rendering fast Vue experiences, but hard-coding content into components doesn't hold up once marketing pages, product detail pages, legal copy, blog posts, and localized routes change on different schedules. Without an integration, developers end up editing arrays in Git, rebuilding for small text changes, or copying content between tools. That works for 10 pages. It gets painful at 500 pages, three locales, and weekly launches.


03Architecture

Architecture overview

A typical Nuxt and Sanity integration starts when an editor publishes in Sanity Studio. That publish updates structured JSON in the Content Lake. A Sanity webhook, filtered with GROQ such as _type in ["page", "post", "product"], sends the changed document ID to a Nuxt Nitro server route like /api/sanity-webhook. The Nuxt route validates the shared secret, uses @sanity/client to fetch the full document with a GROQ projection, writes the result to Nitro storage or clears a cached route, and returns a small JSON response. On the next request, Nuxt renders the updated page through server-side rendering, static regeneration, or useAsyncData in the page component. If you need extra processing before Nuxt sees the update, a Sanity Function can run on the mutation event, fetch related content, transform the payload, and call the Nuxt API endpoint from Sanity's server-side runtime.


04Use cases

Common use cases

🚀

Marketing site launches

Build Nuxt pages from Sanity page documents so campaign copy, hero modules, CTAs, and SEO fields can publish without a developer editing Vue files.

🛒

Product detail pages

Use GROQ to join product content, category references, buying guides, and localized copy before Nuxt renders cached PDP routes.

🌍

Localized static routes

Generate Nuxt routes for en-US, fr-FR, and de-DE from the same schema, then refresh only the locale and slug affected by a publish event.

📚

Docs and resource hubs

Feed Nuxt content collections with structured articles, authors, topics, and related links instead of maintaining Markdown folders by hand.


05Implementation

Step-by-step integration

  1. 1

    Create the Nuxt app and add Sanity packages

    Start with npx nuxi@latest init my-nuxt-site, then install the Sanity client with npm i @sanity/client. If you want Nuxt composables for queries, add npm i @nuxtjs/sanity and configure projectId, dataset, apiVersion, and useCdn in nuxt.config.ts.

  2. 2

    Add environment variables

    Add SANITY_PROJECT_ID, SANITY_DATASET, SANITY_READ_TOKEN, and SANITY_WEBHOOK_SECRET to .env. Use a read token only when your Nuxt server route needs draft or private content. Public published content can use the Content Lake CDN.

  3. 3

    Model content in Sanity Studio

    Define schemas for the routes Nuxt will render. A page schema usually needs title, slug, seoTitle, seoDescription, modules, and references to reusable content such as navigation, authors, categories, products, or media.

  4. 4

    Build the Nuxt data layer

    Create server utilities or composables that call @sanity/client or @nuxtjs/sanity. In page components, use useAsyncData with route.params.slug so Nuxt can render the content on the server, cache it, or hydrate it in the browser.

  5. 5

    Create the publish sync

    In Sanity, create a webhook filtered to the document types that affect Nuxt routes. Point it at a Nitro endpoint such as /api/sanity-webhook and send the document ID plus a shared secret. For heavier work, move the logic into a Sanity Function and have it call the Nuxt endpoint after processing.

  6. 6

    Test route refresh and failure cases

    Publish a draft in Sanity Studio, confirm the webhook fires, inspect the Nuxt server logs, and verify the changed route updates. Also test deleted slugs, renamed slugs, missing references, and webhook retries so old pages don't linger in cache.


06Code

Code example

typescriptserver/api/sanity-webhook.post.ts
import { createClient } from '@sanity/client'
import { defineEventHandler, getHeader, readBody, createError } from 'h3'
import { useStorage } from '#imports'

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET || 'production',
  apiVersion: '2025-02-19',
  token: process.env.SANITY_READ_TOKEN,
  useCdn: false
})

export default defineEventHandler(async (event) => {
  const auth = getHeader(event, 'authorization')
  if (auth !== `Bearer ${process.env.SANITY_WEBHOOK_SECRET}`) {
    throw createError({ statusCode: 401, statusMessage: 'Invalid webhook secret' })
  }

  const body = await readBody<{ _id: string }>(event)

  const page = await sanity.fetch(
    `*[_id == $id][0]{
      _id,
      title,
      slug,
      seoTitle,
      seoDescription,
      modules[]{..., _type == "hero" => {heading, subheading}}
    }`,
    { id: body._id }
  )

  if (!page?.slug?.current) return { ok: true, skipped: true }

  await useStorage('cache').setItem(`sanity:page:${page.slug.current}`, page)
  await useStorage('cache').removeItem(`nitro:routes:/pages/${page.slug.current}.json`)

  return { ok: true, slug: page.slug.current }
})

07Why Sanity

How Sanity + Nuxt works

Build your Nuxt integration on Sanity

Sanity gives you the structured content foundation, real-time event system, GROQ queries, and flexible APIs to connect Nuxt to every content update.

Start building free →

08Comparison

CMS approaches to Nuxt

CapabilityTraditional CMSSanity
Nuxt page dataOften sends rendered HTML or page-shaped blobs that Nuxt has to work around.Uses GROQ to return route-ready JSON with references resolved in one query.
Publish-to-route updatesOften depends on full-site rebuilds, manual cache clears, or plugin-specific behavior.Uses GROQ-filtered webhooks or Functions to refresh only the Nuxt routes affected by the content change.
Schema changesContent types are often changed in an admin UI, which can drift from the Nuxt codebase.Defines schemas as code, so fields used by Nuxt can be reviewed, tested, and shipped with the app.
Preview and visual editingPreview often works best inside the original page rendering system.Presentation Tool and Content Source Maps connect rendered Nuxt elements back to source fields for click-to-edit workflows.
Server-side sync logicOften needs plugins, cron jobs, or a separate worker to process content events.Functions can run server-side logic on content mutations, then call Nuxt without running a separate queue or worker.
AI-ready contentPage blobs make it harder for agents to retrieve exact fields and relationships.Agent Context gives production AI agents scoped, schema-aware access to the same structured content that Nuxt renders.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Nuxt and 200+ other tools.