iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🌐

Introduction to Practical Headless CMS Development with Next.js (App Router) and microCMS

に公開

Introduction

Using Next.js (App Router) and microCMS, I have implemented a "simple Headless CMS architecture ready for production use."

In this article, I will use this implementation as a subject to organize and explain the following points:

  • How to approach CMS architecture in the App Router era
  • How to design microCMS for easy maintenance
  • How to choose between SSG and ISR

...based on the actual implementation.


What we will build

  • A CMS frontend using Next.js (App Router)
  • Article management using microCMS
  • Article index and detail pages
  • High-speed delivery using SSG / ISR

The completed version will look like a minimal blog/media site, suitable for:

  • Personal blogs
  • Technical blogs
  • Small-scale owned media
  • "News" features on corporate sites

Target Audience

This article is intended for those who:

  • Have started using Next.js App Router but are unsure about the design
  • Are using microCMS "just because" but don't know the best practices
  • Want to introduce and operate a Headless CMS in a professional environment
  • Want to properly understand the differences between SSG and ISR

Assumes basic knowledge of Next.js / React.


Sample Code

The implementation explained in this article is available in the following GitHub repository:

https://github.com/xinome/nextjs-headlesscms-web-sample

Following along with the code will make it easier to understand.


Overall Architecture and Data Flow

First, let's get a rough overview of the architecture.

System Diagram

Key Points

  • Users only access Next.js
  • microCMS is API-only (no frontend UI)
  • Next.js handles data fetching and screen rendering
  • Displays are fast due to SSG / ISR

Data Flow (Page Rendering Process)

Notes

  • Only accesses microCMS during initial build or revalidation
  • No API traffic during the cache period
  • An architecture well-suited for CMS content with low update frequency

Technologies and Design Policy

Technologies Used

  • Next.js (App Router)
  • TypeScript
  • microCMS
  • SSG / ISR
  • Vercel (Deployment target)

Why use App Router?

Since Next.js 13, the App Router has become the standard.

App Router offers benefits such as:

  • Server Components by default
  • Tightly integrated data fetching and rendering
  • Natural support for SSG / ISR

The point is that it is highly compatible with CMS.


Why use microCMS?

microCMS is characterized by:

  • Extensive Japanese documentation
  • Simple management console
  • Easy-to-understand APIs
  • Suitability for small to medium-scale sites

It is a service that is easy to handle even for those "new to Headless CMS."


What this article does not cover

This article will not cover the following:

  • Authentication / Login features
  • Customizing the management console
  • Preview features
  • Advanced SEO tuning

We will focus on the "minimal architecture for reliable operations."


Setting up microCMS

Creating a Service

Log in to microCMS and create a new service.


Designing the Content Model

We will prepare the following two models:

posts

  • title (text)
  • content (rich editor)
  • category (reference)
  • publishedAt (publication date)

categories

  • name (text)
  • slug (text)

Note: We will use slug for the URL.


API Settings

  • Issue a Read API key
  • Configure settings to retrieve only published content

👉 We will only use the Read API from the Next.js side.


Next.js Project Creation and Structure

Creating the Project

npx create-next-app@latest
  • Select App Router
  • Enable TypeScript

Directory Structure

src/
├─ app/
│  ├─ layout.tsx
│  ├─ page.tsx              // Article index
│  └─ posts/
│     └─ [slug]/
│        └─ page.tsx        // Article detail
│
├─ libs/
│  └─ microcms.ts           // microCMS client
│
├─ types/
│  └─ microcms.ts           // Type definitions

We will proceed with the implementation based on this structure.


Implementing the microCMS Client

First, we will implement the client to access microCMS from the Next.js side.

Installing the microCMS SDK

npm install microcms-js-sdk

Defining the Client

Create libs/microcms.ts.

import { createClient } from "microcms-js-sdk";

if (!process.env.MICROCMS_SERVICE_DOMAIN) {
  throw new Error("MICROCMS_SERVICE_DOMAIN is required");
}

if (!process.env.MICROCMS_API_KEY) {
  throw new Error("MICROCMS_API_KEY is required");
}

export const client = createClient({
  serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: process.env.MICROCMS_API_KEY,
});

Key Points

  • Always perform environment variable checks.
  • It makes it easier to notice configuration errors since you can throw errors during build time.
  • Since Server Components are the default in App Router, this implementation is sufficient.

Type Definitions

Prepare types/microcms.ts.

import type { MicroCMSDate, MicroCMSImage } from "microcms-js-sdk";

export type Category = {
  name: string;
  slug: string;
};

export type Post = {
  title: string;
  content: string;
  category: Category;
} & MicroCMSDate;

👉 It is easier to manage microCMS types by starting with the "minimum necessary and adding more as needed."


Implementing the Article Index Page (SSG / ISR)

On the top page, we will display the article index.

Data Fetching Logic

Implement this in app/page.tsx.

import { client } from "@/libs/microcms";
import type { Post } from "@/types/microcms";

export const revalidate = 60;

export default async function Home() {
  const data = await client.getList<Post>({
    endpoint: "posts",
  });

  return (
    <main>
      <h1>Article Index</h1>
      <ul>
        {data.contents.map((post) => (
          <li key={post.id}>
            <a href={`/posts/${post.id}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </main>
  );
}

Thinking about SSG / ISR

  • revalidate = 60
    → ISR that regenerates every 60 seconds.
  • Completely statically generated during the initial build.
  • No access to microCMS during the cache period.

Guidelines for production use

Update Frequency revalidate
Rarely updated Several hours to 1 day
Occasional updates Several minutes to tens of minutes
Frequent updates 60 seconds or less

Implementing the Article Detail Page

Next, we will create the article detail page.

Directory Structure

app/posts/[slug]/page.tsx

Static Path Generation

import { client } from "@/libs/microcms";
import type { Post } from "@/types/microcms";

export async function generateStaticParams() {
  const data = await client.getList<Post>({
    endpoint: "posts",
  });

  return data.contents.map((post) => ({
    slug: post.id,
  }));
}

Fetching and Displaying Article Details

export const revalidate = 60;

export default async function PostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await client.get<Post>({
    endpoint: "posts",
    contentId: params.slug,
  });

  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

Points to note:

  • dangerouslySetInnerHTML assumes you are using the microCMS rich editor.
  • Consider sanitization if handling external inputs.

Category Design and Expansion

When using categories, the following design is generally used.

URL Design Example

  • /categories/[slug]
  • /posts/[slug]

Fetching Articles by Category (Example)

await client.getList<Post>({
  endpoint: "posts",
  queries: {
    filters: `category.slug[equals]${slug}`,
  },
});

Key Point:

  • Always consider URL design and CMS design as a set.
  • Changing them later will have a significant impact on SEO.

Common Pitfalls in App Router

Difference between Server and Client Components

  • Need useState / useEffect? → Client Component
  • Data fetching only? → Server Component

👉 It is simpler to put CMS integration parts into Server Components.


Causes of Build Errors

  • Unconfigured environment variables
  • Incorrect return value from generateStaticParams
  • Type mismatches

👉 Check the build logs first.


Use Cases in Production

This architecture can be used directly for:

  • Technical blogs
  • Corporate websites
  • News / Announcement pages
  • Small-scale owned media

Ideas for Future Expansion

  • Preview features
  • Draft support
  • Pagination
  • SEO (Metadata API)
  • Management console integration

Conclusion

By combining Next.js (App Router) and microCMS, you can build a Headless CMS architecture that is:

  • Fast
  • Simple
  • Easy to maintain

My recommendation is to start with a minimal implementation and expand as needed.


References

Discussion