On-demand Sanity CMS setup for editable content. Guides through initialization, schema creation, and Studio configuration. Triggers when user wants to make content editable.
This skill guides users through adding Sanity CMS to make their website content editable through a friendly admin interface.
"Sanity is like a Google Doc for your website. You edit text and images in a friendly dashboard, and your website automatically updates. No coding needed after we set it up."
Key benefits for non-technical users:
If they don't have Sanity set up yet:
"First, let's get you a Sanity account. Go to sanity.io and sign up—it's free for small sites."
Wait for them to confirm account creation.
npx sanity@latest init --env
This will prompt for:
/sanity or /studionpm install next-sanity @sanity/image-url
Create/update .env.local:
NEXT_PUBLIC_SANITY_PROJECT_ID="[from sanity init]"
NEXT_PUBLIC_SANITY_DATASET="production"
NEXT_PUBLIC_SANITY_API_VERSION="2024-01-01"
Create lib/sanity.ts:
import { createClient } from 'next-sanity'
import imageUrlBuilder from '@sanity/image-url'
export const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
apiVersion: process.env.NEXT_PUBLIC_SANITY_API_VERSION!,
useCdn: true,
})
const builder = imageUrlBuilder(client)
export function urlFor(source: any) {
return builder.image(source)
}
Create sanity/schemas/homepage.ts:
import { defineType, defineField } from 'sanity'
export const homepage = defineType({
name: 'homepage',
title: 'Homepage',
type: 'document',
fields: [
defineField({
name: 'heroHeadline',
title: 'Hero Headline',
type: 'string',
description: 'The main headline at the top of the page',
}),
defineField({
name: 'heroSubheadline',
title: 'Hero Subheadline',
type: 'text',
rows: 2,
description: 'The smaller text below the headline',
}),
defineField({
name: 'heroCtaText',
title: 'Button Text',
type: 'string',
description: 'Text on the main call-to-action button',
}),
defineField({
name: 'heroCtaLink',
title: 'Button Link',
type: 'string',
description: 'Where the button goes when clicked',
}),
defineField({
name: 'heroImage',
title: 'Hero Image',
type: 'image',
options: { hotspot: true },
}),
],
preview: {
prepare() {
return { title: 'Homepage' }
},
},
})
Create sanity/schemas/feature.ts:
import { defineType, defineField } from 'sanity'
export const feature = defineType({
name: 'feature',
title: 'Feature',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Feature Title',
type: 'string',
}),
defineField({
name: 'description',
title: 'Description',
type: 'text',
rows: 3,
}),
defineField({
name: 'icon',
title: 'Icon Name',
type: 'string',
description: 'Icon identifier (e.g., "check", "star", "shield")',
}),
defineField({
name: 'order',
title: 'Display Order',
type: 'number',
description: 'Order to display (1 = first)',
}),
],
orderings: [
{
title: 'Display Order',
name: 'orderAsc',
by: [{ field: 'order', direction: 'asc' }],
},
],
})
Create sanity/schemas/testimonial.ts:
import { defineType, defineField } from 'sanity'
export const testimonial = defineType({
name: 'testimonial',
title: 'Testimonial',
type: 'document',
fields: [
defineField({
name: 'quote',
title: 'Quote',
type: 'text',
rows: 4,
}),
defineField({
name: 'authorName',
title: 'Author Name',
type: 'string',
}),
defineField({
name: 'authorTitle',
title: 'Author Title',
type: 'string',
description: 'e.g., "CEO at Company"',
}),
defineField({
name: 'authorImage',
title: 'Author Photo',
type: 'image',
options: { hotspot: true },
}),
defineField({
name: 'featured',
title: 'Featured',
type: 'boolean',
description: 'Show this testimonial prominently',
}),
],
})
Update sanity/schema.ts:
import { type SchemaTypeDefinition } from 'sanity'
import { homepage } from './schemas/homepage'
import { feature } from './schemas/feature'
import { testimonial } from './schemas/testimonial'
export const schema: { types: SchemaTypeDefinition[] } = {
types: [homepage, feature, testimonial],
}
// app/page.tsx
import { client } from '@/lib/sanity'
async function getHomepageContent() {
return client.fetch(`
*[_type == "homepage"][0] {
heroHeadline,
heroSubheadline,
heroCtaText,
heroCtaLink,
heroImage
}
`)
}
export default async function Home() {
const content = await getHomepageContent()
return (
<section>
<h1>{content?.heroHeadline || 'Welcome'}</h1>
<p>{content?.heroSubheadline}</p>
{/* ... */}
</section>
)
}
import { urlFor } from '@/lib/sanity'
// In component:
{content?.heroImage && (
<img
src={urlFor(content.heroImage).width(1200).url()}
alt=""
/>
)}
cd sanity && npm run dev
Studio runs at http://localhost:3333
"Now you have an editing dashboard! Go to localhost:3333 in your browser. You'll see your content types (Homepage, Features, Testimonials). Click on one to edit it, then publish your changes."
sanity/schemas/[name].tssanity/schema.tscd sanity && npx sanity deploy
This gives them a hosted Studio URL they can bookmark.
After setup, explain:
"Here's how to update your website now:
- Go to [Studio URL] and log in
- Click on 'Homepage' (or whatever you want to edit)
- Change the text or images
- Click 'Publish' in the bottom right
- Your website updates automatically!
You can't break anything—worst case, just undo your changes."