When I needed to integrate a blog into this portfolio, I had one non-negotiable requirement: the content had to be consumed directly from an API. No traditional databases, no rich-text editors injecting messy HTML and breaking the layout. I work with Next.js and TypeScript every day; I needed a tool that spoke the same language and gave me total control over the code.
That's when I found Sanity.
Sanity defines itself as a Content Operating System. I decided to polish the notes I took while studying it and leave them here as a resource. If you build with Next.js and want to set up your own space, here is what you can do and how it works.
The architecture: separate to control
The advantage of this ecosystem is where everything lives. It's not all mixed up on a dark server:
- →Sanity Studio: This is your admin panel. It's built in React, but you embed it directly inside your local Next.js application.
- →Content Lake: This is Sanity's cloud. This is where your documents and media assets are actually stored.
- →GROQ: This is their query language. Think of it as SQL, but specifically designed to filter and project JSON objects.
The flow is simple: you configure the Studio in your local code, you (or your editors) create content that travels to the Content Lake, and your frontend consumes it by querying with GROQ.
"Live by default": The Next.js setup
The classic way to fetch posts in Next.js involved manual fetching and handling cache revalidation. Not anymore.
Today, the recommended approach is using sanityFetch in your pages, combined with a <SanityLive> component in your frontend layout. The result? Your site's content updates automatically every time you hit publish in the Studio, without you ever having to manually touch or purge the cache.
The end of injected HTML: Portable Text
The biggest headache with CMSs is rich text. Sanity solves this with Portable Text.
Instead of saving a massive block of HTML, it saves everything (paragraphs, lists, images) as an array of structured and queryable JSON objects. To render this in Next.js, you use the <PortableText> component from the next-sanity library.
The best part is that you can pass it your own React components. If there's an image block in the text, you can tell <PortableText> to use the native Next.js <Image> component to render it. You have absolute control over the rendering.
Smart on-demand images
When you upload a photo, Sanity doesn't force you to upload three different sizes. It creates an asset document in its cloud with a reference.
In your code, you use a function called urlFor to build dynamic URLs. You request the image with a specific width, height, and quality, and Sanity's global CDN transforms it and delivers it in the most optimal format (like WebP or AVIF) based on the user's browser. All on the fly.
SEO managed from the query
SEO shouldn't clutter your frontend logic. The philosophy here is to have smart defaults.
You can build an seo object in your schema with optional fields. Then, in your GROQ query, you use the coalesce() function (which returns the first non-null value) to decide whether to use the custom SEO title or the standard post title.
Advanced tip: You can use the Next.js Edge Runtime to generate dynamic images for social media (Open Graph), creating gradient backgrounds with the dominant colors that Sanity automatically extracts from your photo's metadata.
Effortless TypeScript
Sanity can automatically generate TypeScript types based on your schemas and GROQ queries. You extract the schema to a JSON file and run npx sanity@latest typegen generate. Set that up as a script in your package.json to run before every build, and your entire content is fully typed end-to-end.
This is the engine that runs my portfolio. If you want to write and publish without the tool getting in the way of your design process, this is the route.
