Codegea

Development

Custom software developmentSystems, platforms and integrations built around your operationCorporate websitesProfessional, fast and built to inspire trustOnline storesCatalog, payments and shipping ready to sell 24/7WooCommerceFast WooCommerce stores with each country’s payments and invoicingLanding pagesA page focused on one goal: turning visitors into customersMobile appsCustom iOS and Android apps that put your business in your customers’ pockets

Integration and CMS

CMS integrationsWordPress, Strapi, Directus and Arc XP connected to your whole ecosystemArc XPSupport, implementation and integrations for the media platformMigrationsMigrate your site, platform or database without losing anything

Growth and infrastructure

AI automationTechnology does the repetitive work and your team focuses on what mattersOn-page SEOBe found and recommended by Google: technical and content optimizationServers & hostingFast, secure servers monitored 24/7
RetainerYour dev team, by the monthFrom 20h a month. Fixed fee.
An AI quoting tool to quote services in minutesProfessional quotes in minutes, with projects, sellers and payments tracked in one place.See the case

By type

EcommerceStores and catalogsCorporate sitesBrand and lead genPlatforms & appsCustom software

Explore

All projectsCases and resultsClientsWho trusts usRetainerMonthly dev team

Blog

All articlesGuides and opinionNewsletterOne email a monthContactWe reply in 24h

Latest articles

Integrating Piano, Cxense and Google Ad Manager with Arc XPCMS · 2026-10-13How to migrate from WordPress to Arc XP without losing SEOCMS · 2026-10-13Arc XP vs WordPress for a digital media outlet: when to make the jumpCMS · 2026-10-13
NewsletterJoin our newsletterPractical web, SEO and AI notes.
ServicesCustom software developmentCorporate websitesOnline storesWooCommerceLanding pagesMobile appsCMS integrationsArc XPMigrationsAI automationOn-page SEOServers & hostingYour dev team, by the month
ProjectsProfessional quotes in minutes, with projects, sellers and payments tracked in one place.EcommerceCorporate sitesPlatforms & appsAll projectsClientsRetainer
Retainer
BlogAll articlesNewsletterContactIntegrating Piano, Cxense and Google Ad Manager with Arc XPHow to migrate from WordPress to Arc XP without losing SEOArc XP vs WordPress for a digital media outlet: when to make the jumpJoin our newsletter
ESEN
Let's talk

CMS

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.

Derian Pinto · Founder & Full Stack Developer2026-07-043 min
How to integrate Keystatic into Next.js step by step

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. The steps below follow the official Keystatic documentation.

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

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.

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.

// 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.

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.

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.

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.

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.

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.


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 service or write to us from the contact page.

Share this article?LinkedInWhatsAppX

Written by

Derian Pinto

Founder & Full Stack Developer

Founder of Codegea. Builds custom websites, stores and platforms, and integrates and supports Arc XP for media companies.

Join our newsletter

An email with news and practical web, SEO and AI notes. No spam, unsubscribe anytime.

Shall we build your project?

Need help? Chat with us