Headless CMS with Nuxt/Next.js: integration via API
A Nuxt headless CMS setup gives you full control of the front end and fast server-rendered pages. We show how to connect Nuxt and Next.js to an API securely, where to keep the key, how to cache responses and how to refresh content without webhooks.
A Nuxt headless CMS setup means your content is edited in a CMS panel, while a Nuxt front end fetches it through an API and renders pages on the server. A headless CMS is a content management system without its own presentation layer – it stores and delivers content, and your code decides how the site looks. This guide shows how to integrate Nuxt (and Next.js) with an API securely, where to keep the key, how to cache responses and how to refresh content.
Key takeaways
- In a Nuxt headless CMS architecture, the browser talks to your Nuxt server, and only the server calls the CMS API.
- The API key should be used on the server only and never shipped in code sent to the browser.
- In Nuxt, a private key lives in runtimeConfig outside the public section, and CMS requests run in server routes.
- Server-side response caching shortens load times and reduces the number of API requests.
- Without webhooks, content is refreshed on a timer (for example every few minutes) or by rebuilding the site.
How a Nuxt headless CMS setup works
The data flow in a typical Nuxt headless CMS deployment looks like this:
- An editor publishes content in the CMS panel.
- A visitor opens a page – the request reaches your Nuxt server (or the edge, if you deploy there).
- The server fetches content from the API with the key in a header, or serves a cached response.
- Nuxt renders HTML and sends it to the browser, where the app takes over interaction.
Thanks to server-side rendering, search engines get complete HTML with content, and users see the page sooner. The same model works in Next.js – only the syntax changes.
Keep the API key on the server
This is the most important rule of the whole integration. Anything sent to the browser can be seen by any visitor: in the page source, in developer tools or in network traffic. Even a key for a read-only API should not be public – it lets anyone pull all of your project's published content without visiting your site and consume the API on your behalf.
- in Nuxt, store the key in
runtimeConfig, but not inruntimeConfig.public; - in Next.js, never use the
NEXT_PUBLIC_prefix for the key – those variables end up in client code; - do not call the CMS API directly from components that may run in the browser, for example during client-side navigation;
- add
.envfiles to.gitignoreand set the key as an environment variable on your host in production; - if the key leaks, revoke it in the panel and generate a new one.
Integrating Nuxt with a headless CMS step by step
Say your CMS has a collection with the code projects. First, configure the private key and the API base URL:
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// private key: available on the server only,
// value comes from the NUXT_LESSCMS_API_KEY env variable
lesscmsApiKey: '',
lesscmsBase: 'https://api.lesscms.io/v1/my-workspace/my-project'
}
})Next, create a server route that is the only thing talking to the CMS. defineCachedEventHandler caches the response on the server, and timeout keeps the page from hanging when the API responds slowly:
// server/api/projects.get.ts
export default defineCachedEventHandler(async () => {
const { lesscmsApiKey, lesscmsBase } = useRuntimeConfig()
return $fetch(`${lesscmsBase}/collections/projects`, {
headers: { 'x-api-key': lesscmsApiKey },
timeout: 10000
})
}, { maxAge: 600 }) // response cached on the server for 10 minutesThe page now only calls your own endpoint, so the key never leaves the server – including during client-side navigation:
// pages/projects/index.vue (<script setup> excerpt)
const { data: projects, error } = await useFetch('/api/projects')Handle error in the template: show a message or the last known version of the content instead of an empty page.
The same pattern in Next.js
In Next.js with the App Router, Server Components run only on the server, so you can read the environment variable directly:
// app/projects/page.tsx – Server Component
const BASE = 'https://api.lesscms.io/v1/my-workspace/my-project'
async function getProjects() {
const res = await fetch(`${BASE}/collections/projects`, {
headers: { 'x-api-key': process.env.LESSCMS_API_KEY! },
next: { revalidate: 600 } // revalidate every 10 minutes
})
if (!res.ok) throw new Error(`LessCMS API: ${res.status}`)
return res.json()
}
export default async function Page() {
const projects = await getProjects()
return <ProjectList items={projects} />
}If a Client Component needs the data, expose it through a Route Handler (app/api/.../route.ts) that acts as a server-side proxy.
Caching and content refresh without webhooks
Some headless CMSs send a webhook on publish, which lets you refresh a specific page instantly. If yours does not, pick one of these strategies:
| Strategy | How it works | When to use it |
|---|---|---|
| SSR without cache | Every request calls the API | Low traffic, content must always be current |
| Time-based cache (SWR, ISR) | A response lives for e.g. 5–10 minutes, then refreshes in the background | Most business sites and blogs |
| Static generation | The whole site is built on deploy | Infrequent changes; publishing requires a rebuild |
Cache lifetime is a trade-off: the shorter it is, the sooner changes appear, but the more API requests you make. For a typical business site, a few minutes is a sensible starting point. In a Nuxt headless CMS project, set it in the server route (maxAge) or globally via routeRules with the swr option.
SEO in a headless project
In a Nuxt headless CMS setup, your front end owns SEO. Set meta tags with useSeoMeta in Nuxt or generateMetadata in Next.js, based on fields fetched from the API. Do not forget the sitemap, robots.txt and 301 redirects: if the CMS exposes them via the API, your front end can read them instead of maintaining them by hand.
Common integration mistakes
The same mistakes come up again and again in Nuxt headless CMS projects:
- the key in
runtimeConfig.publicor aNEXT_PUBLIC_variable; - no timeout or error handling – an API outage produces an empty page;
- no caching – every page view triggers a CMS request;
- hard-coded URLs instead of routes fetched from the CMS.
How LessCMS handles it
- A read-only public API returns published content only: pages, collections, menus, blocks, elements, redirects, sitemap, robots, routes and config. The collection endpoint looks like
https://api.lesscms.io/v1/{workspace}/{project}/collections/{code}, and full Swagger documentation is available at/api-docs. - Per-project API keys go in the
x-api-keyheader and can be revoked at any time. Responses include Cache-Tag headers. - LessCMS does not send webhooks, so in a Nuxt headless CMS project use a time-based cache (SWR or ISR) or a rebuild.
- You do not have to build a front end: LessCMS can also render the whole site itself (SSR on Nuxt) on your domain, with automatic Cloudflare cache purging on publish.
Read more about modelling collections on the content structure page, and about working with developers on our page for developers.
Frequently asked questions
Is Nuxt a good fit for a headless CMS?
Yes, a Nuxt headless CMS setup is a popular choice because the framework has built-in server-side rendering, server routes and caching. That lets you hide the API key and serve complete HTML to search engines.
Can I fetch headless CMS data directly in the browser?
Technically yes, but then your API key becomes public. It is safer to call the CMS from the server and expose only the finished data to the browser through your own endpoint.
How do I refresh a Nuxt site after publishing content in the CMS?
The simplest way is a short cache lifetime, such as a few minutes, so new content appears automatically. If your CMS sends webhooks, you can also purge the cache immediately on publish.
Is a headless CMS better for SEO than a traditional one?
Not by definition – SEO depends on rendering and implementation quality. Headless with SSR can be excellent, but you have to handle meta tags, the sitemap and redirects in the front end.
Do I need my own front end to use LessCMS?
No. You can use LessCMS as a headless CMS with your own Nuxt or Next.js app, or let it render the entire site on your domain.
Want to try the endpoints yourself? Visit the LessCMS API page, then pick a plan on the pricing page – you can sign up without a credit card.