Next js

Module 11: CMS & External Integrations in Next.js 14 with TypeScript

Integrate Sanity, Contentful, or Markdown (MDX) with Next.js App Router

Modern web apps often need dynamic, editable content that’s not hardcoded. In this module, you’ll learn how to:

  • Connect a Headless CMS (e.g., Sanity, Contentful, Strapi)
  • Fetch content using REST or GraphQL APIs
  • Render blog-style posts using MDX (Markdown + JSX)
  • Compare CMS vs MDX and when to use each

🧱 Common Use Cases for CMS in Next.js

  • Marketing sites – update landing page content without code
  • Blogs – easily manage posts, authors, tags
  • Portfolios – update projects dynamically
  • Docs – power help sections with markdown or rich text

✅ Option 1: Integrate Sanity.io (or Contentful)

Step 1: Create a Free CMS Project

Visit:

Set up a new project with a “Blog” schema or use a starter template.


Step 2: Install Client SDK

Sanity:

npm install @sanity/client

Step 3: Set Up Sanity Client

// src/lib/sanity.ts
import { createClient } from '@sanity/client';

export const sanity = createClient({
  projectId: 'your_project_id',
  dataset: 'production',
  apiVersion: '2023-01-01',
  useCdn: true,
});

Step 4: Fetch CMS Data in Server Component

// src/app/blog/page.tsx
import { sanity } from '@/lib/sanity';

type Post = {
  _id: string;
  title: string;
  slug: { current: string };
};

export default async function BlogPage() {
  const posts: Post[] = await sanity.fetch(`*[_type == "post"]`);

  return (
    <ul className="space-y-4 p-4">
      {posts.map((post) => (
        <li key={post._id}>
          <a href={`/blog/${post.slug.current}`} className="text-blue-600 underline">
            {post.title}
          </a>
        </li>
      ))}
    </ul>
  );
}

✅ Contentful or Strapi would work similarly using GraphQL/REST.


✅ Option 2: Use MDX (Markdown + JSX)

Great for developers and static content blogs.

Step 1: Install MDX Support

npm install next-mdx-remote gray-matter remark remark-html

Step 2: Create Blog Posts in Markdown

/content/posts/first-post.mdx
---
title: "First Post"
date: "2025-05-01"
---

# Hello World

This is a **Markdown + JSX** post.

<MyCustomComponent />

Step 3: Parse and Render in Server Component

// src/app/blog/[slug]/page.tsx
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { MDXRemote } from 'next-mdx-remote/rsc';

const POSTS_DIR = path.join(process.cwd(), 'content/posts');

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const filePath = path.join(POSTS_DIR, `${params.slug}.mdx`);
  const raw = fs.readFileSync(filePath, 'utf-8');
  const { content, data } = matter(raw);

  return (
    <article className="p-8 max-w-2xl mx-auto prose">
      <h1>{data.title}</h1>
      <MDXRemote source={content} />
    </article>
  );
}

✅ CMS vs MDX – Which Should You Use?

Use Case Choose This
Marketing site / editors Headless CMS
Dev blog / docs MDX
Dynamic user-generated CMS + DB
Custom interactivity MDX with components

✅ Summary

Feature Tool / Example
CMS Integration Sanity, Contentful, Strapi
Markdown Content next-mdx-remote + gray-matter
Render MDX with JSX <MDXRemote source={content} />
Best for blogs/docs MDX with static generation
Best for live editors Sanity/Contentful (REST or GraphQL)

🔜 Coming Up Next:

In Module 12, you’ll build a complete real-world project — an E-Commerce app or Developer Portfolio — using everything learned so far, including auth, CRUD, global state, and deployment.

Would you like this module also converted into Markdown blog format?

Leave a Reply

Your email address will not be published. Required fields are marked *