So, you want to build a website. Not just any website, but one that you can actually update without having to beg a developer every time you want to change a sentence or add a photo. You've probably heard the term "CMS" (Content Management System) thrown around. Think of it as the command center for your website's content.
Today, we're diving into Sanity.io. It's what's known as a "headless CMS." That sounds complicated, but here's the simple truth: instead of your website's content and the way it looks being welded together (like in WordPress), a headless CMS keeps them separate. This gives you incredible power and flexibility . You manage your content in a beautiful, easy-to-use editing room called the Sanity Studio, and then you, or a developer, can display that content anywhere—on a website, an app, or anything else .
Think of it like this: Sanity is the backstage area where all your props and scripts are organized, and your website is the stage where the performance happens. They are connected, but changing a script doesn't collapse the stage.
In this guide, we'll build your first website using Sanity and Next.js, a popular framework from Vercel that's a match made in heaven for this kind of project . We'll also lay the groundwork for SEO right from the start, because what's the point of a beautiful site if no one can find it?
Let's get started. We're going to build something you can be proud of.
This is where you'll spend your time writing blogs, creating pages, and uploading images. Let's get it set up.
First, head over to manage.sanity.io and create a free account. You can sign in with Google, GitHub, or your email .
Don't let the name scare you. This is just a tool that lets you talk to Sanity from your computer's terminal. Open your terminal (Mac) or command prompt (Windows) and run this command:
bash
npm install -g @sanity/cli
This installs it globally on your computer, meaning you only have to do it once .
Now, let's create a folder for your project and set up Sanity inside it.
mkdir my-awesome-website && cd my-awesome-websitesanity initAnd just like that, you have a fully functional CMS backend! You'll see a new folder called studio (or similar) inside your project.
Let's see your new command center. In the terminal, navigate into the studio folder and start it up:
bash
cd studiosanity dev
This will open up http://localhost:3333 in your browser. This is your Sanity Studio! Go ahead, click on "Post" and create your first blog post. Give it a title, some content, and hit "Publish" . You're now a content creator!
Your content is safe and sound in Sanity. Now, let's build the "stage"—the website your visitors will actually see. We'll use Next.js.
Open a new terminal window. Don't close the one running your studio! We'll create your website in a separate folder alongside your studio folder.
studio folder, type cd .. to go back.bash
npx create-next-app@latest frontend
src/ directory? No.Once it's done, navigate into your new frontend folder:
bash
cd frontend
Now for the magic—connecting your new website to your content.
bash
npm install @sanity/client @sanity/image-url
@sanity/client is what we use to fetch data, and @sanity/image-url helps us handle images .frontend folder, create a new folder called lib. Inside that, create a file called sanity.js. This will be our connection file.frontend/lib/sanity.js and paste this in. You'll need two pieces of information from your Sanity project: your Project ID and the dataset name.http://localhost:3333).manage.sanity.io and find your project.abc123de) and your Dataset name (probably production).frontend folder called .env.local. Add these lines, replacing the placeholders with your actual info:text
NEXT_PUBLIC_SANITY_PROJECT_ID=your_project_id_hereNEXT_PUBLIC_SANITY_DATASET=production
npm run dev) after creating this file.Let's prove the connection works by listing your blog posts on the homepage.
frontend/app/page.js. Delete all the default code.javascript
// frontend/app/page.jsimport { client } from '@/lib/sanity'async function getPosts() { const query = `*[_type == "post"] | order(publishedAt desc){ title, slug, publishedAt, body }` const posts = await client.fetch(query) return posts}export default async function Home() { const posts = await getPosts() return ( <main> <h1>My Awesome Blog</h1> {posts.map((post) => ( <div key={post.slug.current}> <h2>{post.title}</h2> {/* We'll link to the full post later */} </div> ))} </main> )}
http://localhost:3000 and you should see your blog post title!This is where we make sure Google can not only find your site but also understand it and show it to the right people. We'll build SEO right into your content model .
Instead of having SEO fields scattered everywhere, we create a single, reusable "object" in Sanity. This is the core of a great SEO strategy .
sanity dev) with Ctrl+C.studio/schemaTypes folder.seo.js.javascript
// studio/schemaTypes/seo.jsimport { defineField, defineType } from 'sanity'export default defineType({ name: 'seo', title: 'SEO', type: 'object', fields: [ defineField({ name: 'metaTitle', title: 'Meta Title', type: 'string', description: 'Title for search engines (recommended length: 50-60 characters)', validation: Rule => Rule.max(60).warning('Should be under 60 characters') }), defineField({ name: 'metaDescription', title: 'Meta Description', type: 'text', rows: 3, description: 'Description for search engines (recommended length: 150-160 characters)', validation: Rule => Rule.max(160).warning('Should be under 160 characters') }), defineField({ name: 'noindex', title: 'Hide from search engines', type: 'boolean', description: 'Check this if you do NOT want this page to appear in search results.', initialValue: false }), ]})
Now, let's add this new SEO object to your blog post schema.
studio/schemaTypes/post.js.import seo from './seo'fields array, add the SEO object as a field. Your fields might now look something like this:javascript
// ... inside post.jsfields: [ // ... other fields like title, slug, etc. ... defineField({ name: 'title', title: 'Title', type: 'string', }), defineField({ name: 'seo', title: 'SEO', type: 'seo', // This references the object we just made }), // ... more fields ...],
sanity dev). Now, when you edit a post, you'll have a neat "SEO" section where you can fill in your meta title and description.Finally, we need to tell your Next.js site to use this new SEO information. We'll update our blog post page to use these fields in the <head> of the document.
frontend/app, create a new folder called blog. Inside that, create a file called [slug]/page.js. The brackets make it dynamic .frontend/app/blog/[slug]/page.js:javascript
// frontend/app/blog/[slug]/page.jsimport { client } from '@/lib/sanity'async function getPost(slug) { const query = `*[_type == "post" && slug.current == $slug][0]{ title, body, "seo": seo { metaTitle, metaDescription, noindex } }` const post = await client.fetch(query, { slug }) return post}export async function generateMetadata({ params }) { const post = await getPost(params.slug) // Use SEO fields if they exist, otherwise fall back to the post title return { title: post?.seo?.metaTitle || post?.title || 'Blog Post', description: post?.seo?.metaDescription || 'Read this blog post', robots: post?.seo?.noindex ? 'noindex' : 'index', }}export default async function BlogPost({ params }) { const post = await getPost(params.slug) return ( <article> <h1>{post.title}</h1> <div>{/* Render your post body here */}</div> </article> )}
generateMetadata. Next.js automatically takes the metadata we return and puts it in the <head> of the page, giving you perfect, per-post SEO control .For a truly findable site, you need a few more things. These are tactics that the best websites use .
sitemap.js file in your app folder. Inside, you'd write a function that uses the Sanity client to fetch all your post slugs and page slugs, then formats them into an XML sitemap for search engines .urlFor function we set up earlier to transform your images on the fly—resize them, change their format to modern webp, and adjust quality. And never forget alt text. Add an alt field to your image schemas in Sanity so your content editors can write descriptive text, which is crucial for accessibility and image search .Your site works on your computer. Now, let's put it on the internet. We'll use Vercel, the company that made Next.js, because it's incredibly easy.
.env.local file: NEXT_PUBLIC_SANITY_PROJECT_ID and NEXT_PUBLIC_SANITY_DATASET with their values.manage.sanity.io, find your project, and go to the API tab.https://your-site.vercel.app). You can leave the "Allow credentials" checkbox unchecked for now .Once the deploy finishes, Vercel will give you a live URL. Visit it. That's your website, live on the internet, powered by your content from Sanity.
Congratulations! You've just built a modern, scalable, and SEO-friendly website. You're no longer just a consumer of the web; you're a creator.
You've taken a huge step. You set up a flexible CMS, connected it to a powerful frontend, and built a solid SEO foundation that many developers overlook. From here, the possibilities are endless. You can create new page types, add more complex components to your Sanity Studio, and fine-tune your site's design.
The web is yours. Go build something awesome.
On this blog, I write about what I love: AI, web design, graphic design, SEO, tech, and cinema, with a personal twist.



