Vue developers have unique needs. You want a CMS that works naturally with the Composition API, plays nice with useFetch and useAsyncData, and doesn't force React patterns onto your Vue codebase. Let's find the right fit.
Nuxt 4 CMS Requirements
Not every headless CMS is created equal for Nuxt. Here's what actually matters:
Server-Side Rendering Compatibility
Nuxt's power comes from hybrid rendering. Your CMS SDK should work in both server and client contexts without hydration mismatches. Some CMSs have SDKs that assume browser-only execution and break during SSR.
Composable-Friendly API
The Composition API is central to Vue 3 and Nuxt 4. You want a CMS client that fits naturally into composables, not one that fights against the pattern.
Integration with useFetch/useAsyncData
Nuxt's data fetching composables handle caching, hydration, and error states. Your CMS should integrate cleanly with these rather than requiring custom solutions.
Nitro/Edge Support
Nuxt 4 can deploy to edge functions via Nitro. Your CMS API calls should work in edge environments without Node.js-specific dependencies.
1. NomaCMS
NomaCMS is our managed, AI-native headless CMS. You get structured content, a REST API, a JavaScript SDK, and AI built in for writing, translation, and editing. Ship to Next.js, Nuxt, Astro, or other stacks without running your own CMS server. Connect Cursor, Claude Code, or other MCP-compatible tools using the Noma MCP server. Plans start at $15 per month after a 7-day free trial; see nomacms.com for tiers and limits.
Why consider NomaCMS
- Managed cloud with global CDN for assets and no server ops on your side
- AI assistant, inline field actions, and one-click entry translation
- REST API and SDK with predictable shapes for frontends and automation
- MCP server for AI editors (
npx -y @nomacms/mcp-server) - Project auth, webhooks, locales, and team workspaces on paid plans
Best for
Teams that want a hosted CMS with AI-first workflows and delivery infrastructure included, not only a self-hosted install.
2. ElmapiCMS
ElmapiCMS is a self-hosted headless CMS built with Laravel. Its simple REST API integrates naturally with Nuxt's data fetching patterns.
Nuxt 4 Integration
The JavaScript SDK works seamlessly with Nuxt's SSR. Here's a composable example:
// composables/useCms.ts
import { createClient } from '@elmapicms/js-sdk';
const client = createClient(
useRuntimeConfig().public.cmsUrl,
useRuntimeConfig().cmsApiKey,
useRuntimeConfig().public.cmsProjectId
);
export function useBlogPosts() {
return useAsyncData('blog-posts', () =>
client.getEntries('posts', {
sort: '-publishedAt',
})
);
}
export function useBlogPost(slug: string) {
return useAsyncData(`post-${slug}`, async () => {
const posts = await client.getEntries('posts', {
filters: { slug },
});
return posts[0] || null;
});
}<!-- pages/blog/index.vue -->
<script setup lang="ts">
const { data: posts, pending, error } = await useBlogPosts();
</script>
<template>
<div>
<h1>Blog</h1>
<div v-if="pending">Loading...</div>
<div v-else-if="error">Error loading posts</div>
<div v-else>
<article v-for="post in posts" :key="post.uuid">
<NuxtLink :to="`/blog/${post.fields.slug}`">
<h2>{{ post.fields.title }}</h2>
</NuxtLink>
<p>{{ post.fields.excerpt }}</p>
</article>
</div>
</div>
</template>Why Choose ElmapiCMS for Nuxt
- Works with useFetch/useAsyncData – Native REST API integrates cleanly
- Multi-project from one installation – Perfect for managing multiple Nuxt sites
- $149 one-time pricing – No monthly fees as your traffic grows
- Self-hosted – Deploy alongside your Nuxt app or separately
- Fast API responses – Laravel's efficiency means quick data fetching
Best For
Agencies building multiple Nuxt sites, developers who prefer self-hosting, and projects needing predictable costs.
3. Storyblok
Storyblok is a cloud CMS with first-class Nuxt support. They have an official Nuxt module and visual editor integration.
Nuxt 4 Integration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@storyblok/nuxt'],
storyblok: {
accessToken: process.env.STORYBLOK_TOKEN,
},
});<script setup lang="ts">
const story = await useAsyncStoryblok('home', { version: 'draft' });
</script>
<template>
<StoryblokComponent v-if="story" :blok="story.content" />
</template>Pros
- Official Nuxt module with deep integration
- Visual editor for content teams
- Component-based content modeling
- Good documentation for Nuxt
Cons
- Usage-based pricing can be expensive
- Cloud-only, no self-hosted option
- Learning curve for the visual editor system
Pricing
Free tier available. Paid plans from $99/month.
4. Strapi
Strapi is the most popular open-source headless CMS. It has a large community of Vue/Nuxt users.
Nuxt 4 Integration
// composables/useStrapi.ts
export function useStrapiPosts() {
const config = useRuntimeConfig();
return useFetch<{ data: Post[] }>('/api/posts', {
baseURL: config.public.strapiUrl,
headers: {
Authorization: `Bearer ${config.strapiToken}`,
},
query: {
populate: '*',
sort: 'publishedAt:desc',
},
});
}Pros
- Open source with large community
- Flexible content modeling
- GraphQL and REST APIs
- Many Nuxt examples available
Cons
- High memory usage (4GB+ recommended)
- No native multi-project support
- Complex deployment and updates
- Response format requires data extraction
Pricing
Open source (free). Strapi Cloud from $18/project/month.
5. Directus
Directus wraps around your database and provides an API layer. Good choice if you have existing data.
Nuxt 4 Integration
// plugins/directus.ts
import { createDirectus, rest } from '@directus/sdk';
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig();
const directus = createDirectus(config.public.directusUrl)
.with(rest());
return {
provide: {
directus,
},
};
});Pros
- Works with existing SQL databases
- No vendor lock-in for data
- Real-time subscriptions
- Open source
Cons
- SDK has learning curve
- More complex initial setup
- Can be overkill for simple sites
Pricing
Open source (free). Cloud plans available.
6. Sanity
Sanity is known for its real-time collaboration features. It works with Nuxt though the integration is less native than with React.
Nuxt 4 Integration
// composables/useSanity.ts
import { createClient } from '@sanity/client';
const client = createClient({
projectId: 'your-project-id',
dataset: 'production',
useCdn: true,
apiVersion: '2024-01-01',
});
export function useSanityPosts() {
return useAsyncData('posts', () =>
client.fetch(`*[_type == "post"] | order(publishedAt desc)`)
);
}Pros
- Real-time collaboration
- Powerful query language (GROQ)
- Good image handling
- Generous free tier
Cons
- GROQ learning curve
- Less first-party Nuxt support than React
- Usage-based pricing at scale
Pricing
Free tier available. Growth plan from $15/user/month.
7. Prismic
Prismic offers a Nuxt module with slice-based content modeling. Good for page builder-style content.
Nuxt 3 Integration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/prismic'],
prismic: {
endpoint: 'your-repo-name',
},
});Pros
- Official Nuxt module
- Slice-based content modeling
- Good preview support
- Affordable pricing
Cons
- Slice system has learning curve
- Query API is proprietary
- Cloud-only
Pricing
Free for one user. Paid plans from $15/month per repository.
Comparison Table
| Feature | NomaCMS | ElmapiCMS | Storyblok | Strapi | Directus | Sanity |
|---|---|---|---|---|---|---|
| Nuxt 4 Support | Yes | Yes | Excellent | Yes | Yes | Yes |
| Official Nuxt Module | No (SDK) | No (SDK) | Yes | Community | No (SDK) | No (SDK) |
| Self-Hosted | No (cloud) | Yes | No | Yes | Yes | Limited |
| Multi-Project | Yes (plans) | Yes | No | No | No | No |
| Visual Editor | AI-assisted | No | Yes | No | No | Partial |
| i18n Built-in | Yes | Yes | Yes | Yes | Yes | Yes |
| Starting Price | From $15/mo | $149 once | $99+/mo | Free (OSS) | Free (OSS) | $15+/mo |
Complete Nuxt Integration Example
Here's a full example of setting up ElmapiCMS with Nuxt 4:
1. Configure Environment
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
cmsApiKey: process.env.ELMAPI_API_KEY,
public: {
cmsUrl: process.env.ELMAPI_URL,
cmsProjectId: process.env.ELMAPI_PROJECT_ID,
},
},
});2. Create CMS Composable
// composables/useCms.ts
import { createClient } from '@elmapicms/js-sdk';
export function useCmsClient() {
const config = useRuntimeConfig();
return createClient(
config.public.cmsUrl,
config.cmsApiKey,
config.public.cmsProjectId
);
}
export function usePosts() {
const client = useCmsClient();
return useAsyncData('posts', () =>
client.getEntries('posts', {
sort: '-publishedAt',
filters: { status: 'published' },
})
);
}
export function usePost(slug: MaybeRef<string>) {
const client = useCmsClient();
const resolvedSlug = toValue(slug);
return useAsyncData(`post-${resolvedSlug}`, async () => {
const posts = await client.getEntries('posts', {
filters: { slug: resolvedSlug },
});
return posts[0] || null;
});
}3. Build Your Pages
<!-- pages/blog/index.vue -->
<script setup lang="ts">
const { data: posts, pending } = await usePosts();
useSeoMeta({
title: 'Blog',
description: 'Latest articles and tutorials',
});
</script>
<template>
<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-8">Blog</h1>
<div v-if="pending" class="animate-pulse">Loading...</div>
<div v-else class="grid gap-6 md:grid-cols-2">
<article v-for="post in posts" :key="post.uuid" class="border rounded-lg p-6">
<NuxtLink :to="`/blog/${post.fields.slug}`">
<h2 class="text-xl font-semibold hover:text-primary">
{{ post.fields.title }}
</h2>
</NuxtLink>
<p class="mt-2 text-gray-600">{{ post.fields.excerpt }}</p>
</article>
</div>
</div>
</template><!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: post, error } = await usePost(route.params.slug as string);
if (error.value || !post.value) {
throw createError({ statusCode: 404, message: 'Post not found' });
}
useSeoMeta({
title: post.value.fields.title,
description: post.value.fields.excerpt,
});
</script>
<template>
<article class="container mx-auto px-4 py-8 max-w-3xl">
<h1 class="text-4xl font-bold mb-4">{{ post.fields.title }}</h1>
<div class="prose prose-lg" v-html="post.fields.content" />
</article>
</template>Conclusion
For Nuxt developers, the right CMS depends on your priorities:
Choose ElmapiCMS if: You want self-hosted simplicity, manage multiple Nuxt sites, or need predictable one-time pricing. The REST API works naturally with Nuxt's data fetching.
Choose Storyblok if: Your content team needs visual editing and you have budget for their pricing.
Choose Strapi if: You want open source with a large community. Be prepared for higher server requirements.
Choose Directus if: You have an existing database or need maximum data control.
For agencies and developers building multiple Nuxt projects, ElmapiCMS offers the best combination of simplicity, multi-project management, and cost efficiency. One installation, one update process, unlimited Nuxt sites.