# How to integrate Keystatic into Next.js step by step

> Learn how to integrate Keystatic into Next.js step by step: installation, keystatic.config.ts, the /keystatic panel, reading content with the reader and GitHub mode.

URL: https://codegea.com/en/how-to-integrate-keystatic-into-next-js-step-by-step
CMS · 2026-07-04 · Derian Pinto

A Markdown-based CMS sounds great until it is time to set it up. The good news is that with Keystatic and Next.js the process is short: by the end you will have an editing panel at `/keystatic` that writes `.md` files inside your repository, with no database in between.

This is the step-by-step guide. If you are coming in cold, it helps to be clear first about what a Markdown-based CMS is and why it fits a [headless approach with Next.js](/en/headless-wordpress-with-nextjs-architecture-advantages-and-how-to-start). The steps below follow the [official Keystatic documentation](https://keystatic.com/docs/installation-next-js).

## What we are going to build

- A Keystatic admin panel served at the `/keystatic` route of your own app.
- A *posts* collection stored as Markdown files in `src/content/posts`.
- Reading that content from your pages with the Keystatic *reader*.

## Before you start

- A Next.js project with the App Router (a new one from `npx create-next-app` works).
- Node.js installed and the project versioned in a Git repository.
- Basic terminal knowledge; you need nothing else.

## Step 1: Install the dependencies

```bash
npm install @keystatic/core @keystatic/next @markdoc/markdoc
```

`@keystatic/core` is the core, `@keystatic/next` connects the panel to Next.js and `@markdoc/markdoc` processes the rich Markdown in the body of entries.

## Step 2: Create the configuration file

In the project root create `keystatic.config.ts`. Here you define where content is stored (`storage`) and which collections exist with their field schema.

```ts
import { config, fields, collection } from '@keystatic/core';

export default config({
  storage: {
    kind: 'local',
  },
  collections: {
    posts: collection({
      label: 'Posts',
      slugField: 'title',
      path: 'src/content/posts/*',
      format: { contentField: 'content' },
      schema: {
        title: fields.slug({ name: { label: 'Title' } }),
        content: fields.markdoc({ label: 'Content' }),
      },
    }),
  },
});
```

`storage: { kind: 'local' }` saves the files on your disk while you develop. Further down we switch it to GitHub for production.

## Step 3: Mount the admin panel

Keystatic serves its own interface inside your app. Create these three files under `src/app/keystatic` to render the panel at the `/keystatic` route.

```ts
// src/app/keystatic/keystatic.ts
'use client';
import { makePage } from '@keystatic/next/ui/app';
import config from '../../../keystatic.config';
export default makePage(config);

// src/app/keystatic/layout.tsx
import KeystaticApp from './keystatic';
export default function Layout() {
  return <KeystaticApp />;
}

// src/app/keystatic/[[...params]]/page.tsx
export default function Page() {
  return null;
}
```

## Step 4: Add the API route handler

The panel needs an endpoint to read and write. Create `src/app/api/keystatic/[...params]/route.ts`.

```ts
import { makeRouteHandler } from '@keystatic/next/route-handler';
import config from '../../../../../keystatic.config';

export const { POST, GET } = makeRouteHandler({
  config,
});
```

You can now start the project with `npm run dev` and open `http://127.0.0.1:3000/keystatic`: there is your panel to create entries.

## Step 5: Read and display the content

To render the entries on your site, Keystatic offers the *reader*. With it you access the whole collection or a specific entry by its slug.

```ts
import { createReader } from '@keystatic/core/reader';
import keystaticConfig from '../../keystatic.config';

const reader = createReader(process.cwd(), keystaticConfig);

// All entries in the collection
const posts = await reader.collections.posts.all();

// A specific entry by its slug
const post = await reader.collections.posts.read('my-first-post');
```

The body of each entry is obtained with `post.content()` and rendered with Markdoc (`Markdoc.transform` and `Markdoc.renderers.react`). You have the details in the [reader documentation](https://keystatic.com/docs/reader-api).

## From local mode to GitHub

Local mode is perfect for developing, but in production you will want editors to save changes without touching your machine. That is what GitHub mode is for: Keystatic makes commits directly to your repository. You only change the `storage`.

```ts
storage: {
  kind: 'github',
  repo: { owner: 'your-user', name: 'your-repo' },
},
```

The first time you enter `/keystatic` in GitHub mode, Keystatic guides you to create a GitHub App and connect the repo; that process generates the variables `KEYSTATIC_GITHUB_CLIENT_ID`, `KEYSTATIC_GITHUB_CLIENT_SECRET` and `KEYSTATIC_SECRET` in your `.env`. From then on, anyone with write access to the repository can edit from the panel. The full steps are in the [GitHub mode documentation](https://keystatic.com/docs/github-mode).

## The order that avoids getting stuck

Most Keystatic problems show up when moving to production, not when installing it. That is why it pays to work in this order: first local mode with two or three test entries; then check that the reader reads them and renders them in your pages; and only then move to GitHub.

A frequent trap: local mode writes to disk, so it works on your machine but not on hosting without a persistent file system such as Vercel. In production the panel needs GitHub mode. When you enable it, check that the variables Keystatic generates in your `.env` (`KEYSTATIC_GITHUB_CLIENT_ID`, `KEYSTATIC_GITHUB_CLIENT_SECRET` and `KEYSTATIC_SECRET`) are also loaded in the production environment.

And make clear who can edit: anyone with write permission on the repository will be able to do it from the panel. If the content grows toward thousands of entries or many editors, compare it against a [headless with a database such as Directus or Strapi](/en/directus-vs-strapi-which-headless-cms-to-choose-for-your-project).

---

Want to set up Keystatic (or the CMS that fits best) in your Next.js project without fighting the configuration? At Codegea we do it for you: see the [CMS integration](/en/services/cms-integrations) service or write to us from [the contact page](/en/contact).
