LessTools
LESSCMSVisual page editor with a headless API LESSCOMMERCEStore, PIM and orders in one place LESSSEOGoogle visibility, measured daily — coming soon
One account and one invoice for all of them. Discover LessTools →
← Blog
Technology

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:

  1. An editor publishes content in the CMS panel.
  2. A visitor opens a page – the request reaches your Nuxt server (or the edge, if you deploy there).
  3. The server fetches content from the API with the key in a header, or serves a cached response.
  4. 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 in runtimeConfig.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 .env files to .gitignore and 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 minutes

The 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:

StrategyHow it worksWhen to use it
SSR without cacheEvery request calls the APILow traffic, content must always be current
Time-based cache (SWR, ISR)A response lives for e.g. 5–10 minutes, then refreshes in the backgroundMost business sites and blogs
Static generationThe whole site is built on deployInfrequent 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.public or a NEXT_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-key header 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.

headless cmsnuxtnext.jsapi

Build a site like this yourself

Visual editor, content via API and SEO built in — on every plan.