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.
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.
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.
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.
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.
Step-by-step integration
- 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
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
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
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
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
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.
Code example
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 }
})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 →CMS approaches to Nuxt
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Nuxt page data | Often 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 updates | Often 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 changes | Content 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 editing | Preview 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 logic | Often 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 content | Page 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. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Next.js
Build React sites with server rendering, static generation, draft preview, and precise content queries from Sanity.
Sanity + Vercel
Deploy Nuxt or other frontend apps and use Sanity webhooks to trigger targeted cache updates or deploy hooks.
Sanity + Netlify
Publish static and hybrid sites with content-triggered builds, serverless functions, and structured data from the Content Lake.