Byte Bot
Byte Bot

Nuxt Content vs Sanity: Setup Comparison and Getting Started Guide

Step-by-step setup comparison of Nuxt Content and Sanity CMS. See the exact code and configuration differences to choose the right CMS for your Nuxt project.

Hunter Goram
8 min read

TL;DR: This guide walks through setting up both Nuxt Content and Sanity from scratch. You'll see the exact commands, configuration files, and code needed to get a blog running with each CMS. By the end, you'll understand the setup complexity and developer experience of each approach.

What We're Building

A simple blog with:

  • A list of posts on the homepage
  • Individual post pages with full content
  • Author information on each post
  • TypeScript support throughout

Part 1: Setting Up Nuxt Content

Step 1: Create the Project

npx nuxi init my-blog
cd my-blog
npm install
npm install @nuxt/content

Step 2: Configure Nuxt

nuxt.config.ts
1export default defineNuxtConfig({
2 modules: ['@nuxt/content'],
3 content: {
4 highlight: {
5 theme: 'github-dark'
6 }
7 }
8})

Step 3: Create Your First Post

Create a content directory and add your first markdown file:

content/blog/hello-world.md
1---
2title: Hello World
3description: My first blog post
4author: John Doe
5publishedAt: 2025-01-15
6tags:
7 - getting-started
8 - nuxt
9---
10
11# Hello World
12
13This is my first blog post using Nuxt Content!
14
15## Why Nuxt Content?
16
17Nuxt Content makes it easy to create content-driven websites.

Step 4: Create the Blog List Page

pages/blog/index.vue
1<template>
2 <div>
3 <h1>Blog</h1>
4 <ul>
5 <li v-for="post in posts" :key="post._path">
6 <NuxtLink :to="post._path">
7 {{ post.title }}
8 </NuxtLink>
9 <p>{{ post.description }}</p>
10 </li>
11 </ul>
12 </div>
13</template>
14
15<script setup lang="ts">
16const { data: posts } = await useAsyncData('blog-posts', () =>
17 queryContent('blog')
18 .only(['title', 'description', '_path', 'publishedAt'])
19 .sort({ publishedAt: -1 })
20 .find()
21)
22</script>

Step 5: Create the Post Detail Page

pages/blog/[...slug].vue
1<template>
2 <article>
3 <ContentDoc v-slot="{ doc }">
4 <h1>{{ doc.title }}</h1>
5 <p>By {{ doc.author }} • {{ formatDate(doc.publishedAt) }}</p>
6 <ContentRenderer :value="doc" />
7 </ContentDoc>
8 </article>
9</template>
10
11<script setup lang="ts">
12const formatDate = (date: string) => {
13 return new Date(date).toLocaleDateString('en-US', {
14 year: 'numeric',
15 month: 'long',
16 day: 'numeric'
17 })
18}
19</script>

That's It!

With Nuxt Content, you're done. Run `pnpm dev` and your blog is live. Total setup time: about 5 minutes.

Part 2: Setting Up Sanity

Step 1: Create the Nuxt Project

npx nuxi init my-sanity-blog
cd my-sanity-blog
npm install
npm install @sanity/client @sanity/image-url

Step 2: Create a Sanity Project

npm create sanity@latest -- --template blog --create-project "My Blog" --dataset production

This creates a Sanity Studio with pre-configured blog schemas. You'll get a project ID that you'll need for the next step.

Step 3: Configure the Sanity Client

lib/sanity.ts
1import { createClient } from '@sanity/client'
2import imageUrlBuilder from '@sanity/image-url'
3
4export const client = createClient({
5 projectId: 'your-project-id',
6 dataset: 'production',
7 useCdn: true,
8 apiVersion: '2024-01-01'
9})
10
11const builder = imageUrlBuilder(client)
12export const urlFor = (source: any) => builder.image(source)

Step 4: Define Your Schema (in Sanity Studio)

sanity/schemas/post.ts
1export default {
2 name: 'post',
3 title: 'Post',
4 type: 'document',
5 fields: [
6 {
7 name: 'title',
8 title: 'Title',
9 type: 'string',
10 validation: (Rule: any) => Rule.required()
11 },
12 {
13 name: 'slug',
14 title: 'Slug',
15 type: 'slug',
16 options: { source: 'title' }
17 },
18 {
19 name: 'author',
20 title: 'Author',
21 type: 'reference',
22 to: [{ type: 'author' }]
23 },
24 {
25 name: 'publishedAt',
26 title: 'Published At',
27 type: 'datetime'
28 },
29 {
30 name: 'body',
31 title: 'Body',
32 type: 'array',
33 of: [{ type: 'block' }]
34 }
35 ]
36}

Step 5: Create the Blog List Page

pages/blog/index.vue
1<template>
2 <div>
3 <h1>Blog</h1>
4 <ul>
5 <li v-for="post in posts" :key="post._id">
6 <NuxtLink :to="`/blog/${post.slug.current}`">
7 {{ post.title }}
8 </NuxtLink>
9 </li>
10 </ul>
11 </div>
12</template>
13
14<script setup lang="ts">
15import { client } from '~/lib/sanity'
16
17const { data: posts } = await useAsyncData('posts', () =>
18 client.fetch(`
19 *[_type == "post"] | order(publishedAt desc) {
20 _id,
21 title,
22 slug,
23 publishedAt,
24 "author": author->name
25 }
26 `)
27)
28</script>

Step 6: Create the Post Detail Page

pages/blog/[slug].vue
1<template>
2 <article v-if="post">
3 <h1>{{ post.title }}</h1>
4 <p>By {{ post.author }} • {{ formatDate(post.publishedAt) }}</p>
5 <SanityContent :blocks="post.body" />
6 </article>
7</template>
8
9<script setup lang="ts">
10import { client } from '~/lib/sanity'
11
12const route = useRoute()
13const { data: post } = await useAsyncData(`post-${route.params.slug}`, () =>
14 client.fetch(`
15 *[_type == "post" && slug.current == $slug][0] {
16 title,
17 body,
18 publishedAt,
19 "author": author->name
20 }
21 `, { slug: route.params.slug })
22)
23
24const formatDate = (date: string) => {
25 return new Date(date).toLocaleDateString('en-US', {
26 year: 'numeric',
27 month: 'long',
28 day: 'numeric'
29 })
30}
31</script>

Additional Setup Required

With Sanity, you also need to set up the Portable Text renderer (SanityContent component), configure CORS in your Sanity project settings, and deploy your Sanity Studio. Total setup time: 15-30 minutes.

Setup Comparison Summary

Here's how the two approaches compare:

  • Dependencies: Nuxt Content needs 1 package; Sanity needs 2+ packages plus a separate studio project
  • Configuration: Nuxt Content is 5 lines; Sanity requires client setup, schema definitions, and studio config
  • Content Creation: Nuxt Content uses markdown files; Sanity uses a visual Studio interface
  • Query Syntax: Nuxt Content uses queryContent(); Sanity uses GROQ queries
  • Time to First Post: Nuxt Content takes ~5 minutes; Sanity takes ~20 minutes

Quick Decision Tree

Which Should You Choose?

  1. Will non-developers edit content regularly? Yes → Sanity. No → Continue.
  2. Do you need real-time collaboration? Yes → Sanity. No → Continue.
  3. Is your budget $0? Yes → Nuxt Content. No → Continue.
  4. Do you want git-based version control for content? Yes → Nuxt Content. No → Sanity.
  5. Building a personal blog or docs site? Yes → Nuxt Content.
  6. Building for a client or team? Consider Sanity.

Next Steps

Now that you've seen both setups, here's what to explore next:

For Nuxt Content:

  • Learn MDC syntax for embedding Vue components in markdown
  • Set up content collections with Zod schemas (v3)
  • Explore Nuxt Studio for visual editing

For Sanity:

  • Master GROQ queries for complex data fetching
  • Set up preview mode for draft content
  • Configure image optimization with the Sanity image pipeline

Both options will serve you well—the right choice depends on your specific project needs and team composition. Happy building!

Nuxt Content Config
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content'],
content: {
highlight: {
theme: 'github-dark'
},
markdown: {
toc: { depth: 3 }
}
}
})
Sanity Config
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
export default defineConfig({
projectId: 'your-project-id',
dataset: 'production',
plugins: [structureTool()],
schema: { types: schemaTypes }
})
RequirementNuxt ContentSanity
Account RequiredNoYes (free tier available)
Time to First Content~5 minutes~15 minutes
Dependencies1 package2-3 packages
Configuration Filesnuxt.config.ts onlysanity.config.ts + schema files
Content LocationLocal /content folderCloud Content Lake
Setup Requirements Comparison

Nuxt Content Installation Guide

Step-by-step installation guide for setting up Nuxt Content in your project.

content.nuxt.com

Sanity Getting Started

Official getting started guide for Sanity - from zero to published content.

sanity.io

About the Author

Hunter Goram

COO

I’m Hunter Goram, COO of Byte Bot. I handle the architecture and operations that keep our agency running. FSU grad, robotics enthusiast, and full-stack developer obsessed with efficiency.