March 18, 2026

I Built a Next.js + Sanity CMS Site in 48 Hours — Here's What Actually Worked

A real build diary of shipping a production Next.js 16 site with Sanity CMS, Tailwind CSS 4, and Vercel deployment. No fluff — just the decisions, mistakes, and shortcuts that saved hours.

By Frank Yao

TLDR

I scaffolded a Next.js 16 App Router site with Sanity CMS in under 48 hours of actual build time. The biggest time-savers: skipping create-next-app's default Tailwind (it installs v3, not v4), using Server Components for everything except the mobile nav, and deploying Sanity Studio separately instead of embedding it. The biggest mistake: not defining modular schema objects from day one. Here's the full breakdown.

I Built a Next.js + Sanity CMS Site in 48 Hours — Here's What Actually Worked
Frank Yao

Quick Reference: The Stack That Shipped

Before we dive in, here's exactly what I used and why each piece matters:

Next.js 16.1 (App Router) — Server Components by default, which means less JavaScript shipped to the browser. Tailwind CSS 4.2 — CSS-first configuration with @theme blocks instead of the old tailwind.config.js. Sanity 5.x — Headless CMS with real-time collaborative editing and GROQ query language. Motion 12 — Animation library imported from motion/react, not the old framer-motion package. Vercel — Zero-config deployment with automatic Git integration.

Why I Stopped Fixing the Old Site and Started Fresh

The previous version of frankyao.com was a static HTML site built from a design template. It looked decent on the surface, but under the hood? Broken internal routing, dead CTA buttons that went nowhere, a contact form with no backend, and zero SEO value. Every page had the same meta title. The sitemap listed pages that didn't exist.

I could have patched it. Added a form handler here, fixed a broken link there. But when you're stacking band-aids on a fundamentally broken foundation, you're just creating technical debt you'll pay for later. The time I'd spend debugging someone else's spaghetti code was better spent building something clean from scratch.

The Create-Next-App Trap Nobody Warns You About

Here's something that cost me an hour the first time I ran into it: npx create-next-app@latest --tailwind still installs Tailwind v3.4. Not v4. In March 2026. The --tailwind flag generates a tailwind.config.js file and uses the old @tailwind directives. If you're planning to use Tailwind 4's CSS-first @theme blocks (and you should — they're faster and cleaner), you need to scaffold without --tailwind and install v4 manually.

The fix is straightforward: run create-next-app without the tailwind flag, then npm install tailwindcss @tailwindcss/postcss postcss. Create a postcss.config.mjs with the @tailwindcss/postcss plugin. Then define your design tokens in @theme blocks inside globals.css. No JavaScript config file needed.

Server Components Changed How I Think About Navigation

In the old React mental model, your nav component needs useState for the mobile menu toggle, so the whole thing becomes a client component. That means shipping React to the browser just to render a list of links. With Next.js 16's App Router, I split it differently: the Nav component stays as a Server Component (zero client JavaScript), and only the MobileNav hamburger toggle is a separate Client Component. The nav links, the logo, the desktop layout — all rendered on the server. Only the interactive bit gets 'use client'.

This pattern — Server Component parent with Client Component islands — is the single biggest performance win in modern Next.js. My initial page load went from 127KB of JavaScript to under 40KB.

The Sanity Schema Mistake That Will Haunt You

When I first set up Sanity for a client project, I created flat schemas. Every document type had its own SEO fields, its own image configuration, its own rich text setup. Six months later, I needed to add Open Graph image support to every content type. That meant editing 8 different schema files.

For frankyao.com, I built modular schema objects from day one. An seoFields object that gets referenced by both blogPost and portfolioItem. A portableTextBody object with consistent rich text configuration. A faqItem object reused anywhere I need FAQ sections. When I need to add a field to SEO configuration, I change one file and every content type inherits it.

What I'd Do Differently Next Time

I'd skip page transitions on the first build. The FrozenRouter pattern required for App Router exit animations imports from an internal Next.js API (LayoutRouterContext) that could break between versions. Scroll reveal animations? Those use the stable whileInView API and are worth it from day one. But the cross-page fade transitions added complexity for a subtle visual effect that most users won't consciously notice.

I'd also set up the Sanity webhook for ISR cache invalidation immediately, not as a Phase 8 task. Having to wait 60 seconds to see content changes in production is annoying during development.

Frequently Asked Questions

Is Next.js 16 stable enough for production sites?

Yes. Next.js 16.1 has been stable since early 2026. The App Router is no longer experimental — it's the default and recommended approach. Server Components, the metadata API, and image optimization all work reliably in production.

Should I use Tailwind CSS 4 or stick with v3?

Use v4 for new projects. The CSS-first @theme configuration is faster to build with, produces smaller output, and doesn't require a JavaScript config file. The only reason to stay on v3 is if you're maintaining an existing project with heavy custom plugin usage.

Why host Sanity Studio separately instead of embedding it in Next.js?

Embedding Sanity Studio in your Next.js app couples your content editing environment to your frontend deployment. If your site build breaks, you can't edit content. A standalone Studio at *.sanity.studio deploys independently and is always available.

How much does this stack cost to run?

For a personal brand site: effectively $0/month. Vercel's free tier handles the hosting. Sanity's free plan includes 5M CDN requests and 10K documents. The only cost is your domain name (~$12/year).

Ready to put this into action?

Let's talk about how AI automation and smart digital strategy can drive real results for your business.