You have a site built with Next.js and want to add a blog or an updates page. Instead of editing source code every time a post changes, you want to write and publish from the browser. That is what a headless CMS is for: it keeps managing content separate from displaying the site.

In this guide, you manage posts in genko.me and show a list and detail pages with the Next.js App Router. Finally, you make drafts viewable on the same detail page before they are published. The look of the site stays in Next.js, so it fits into your existing design.

The steps are based on the versions published as of September 17, 2026 (CLI 0.2.3, SDK 0.3.0, React components 0.3.1) and the official quickstart.

What you need

  • A Next.js 16+ App Router project with React 19 that runs

  • Node.js 20.12 or later, in a version your Next.js supports

  • A genko.me account and a workspace where you are the Owner

The examples use TypeScript and the Node.js runtime. File paths assume an app/ layout. With a src/app/ layout, pages go in src/app/ and the files the CLI generates go in src/lib/. Adjust the @/ alias to your project too.

1. Create an API for your posts

In your genko.me workspace, create an API named "Blog" with the endpoint blog and the "List" type. The List type is for content with many entries, such as posts.

Add these two fields to the schema. The code below uses the field IDs, so match the IDs as well as the display names.

Display name

Field ID

Type

Required

Title

title

Text

Yes

Body

body

Rich text

Yes

Next, create one post. Enter a title and body, set the status to "Published" in the editor's "Settings", and save. The regular delivery API does not return drafts, so use a published post to check the connection.

You can also follow the screens in APIs and schemas and Create and publish content.

2. Create an API key and connect with the CLI

From the workspace menu, open "Settings and plan" → "API keys". Create a key just for this site, set its access to "Selected APIs only", and allow blog. Set a validity period too, and copy the key shown right after creation. You cannot view the full key again later.

Your workspace ID is your-workspace if the dashboard URL is https://genko.me/w/your-workspace.

Run this in the directory that contains your Next.js project's package.json.

bash
npx @genko-me/cli init

Enter your workspace ID and API key, and choose blog as the API to sync. The CLI adds the SDK and other dependencies, and generates the schema types and a client for connecting.

  • lib/schema.ts: types for the API and its fields

  • lib/client.ts: the client that connects to genko.me

  • genko.config.json: settings used for the next sync

  • .env.local: where the API key is stored (unless the key is already in an existing environment variable)

Treat the API key as the server-side GENKO_API_KEY. Do not prefix it with NEXT_PUBLIC_ or send it to the browser. Also make sure .env.local is not tracked by Git. For details, see Manage API keys.

The CLI does not generate pages. Add the next two files to your project yourself.

3. Show a list of posts

Create app/blog/page.tsx. This page is a server component, so do not add "use client" at the top.

tsx
import { client } from "@/lib/client";import { List, ListItem, Pagination, Error, Null } from "@genko-me/react/next";
export default function BlogPage() {  return (    <main>      <h1>Blog</h1>      <List api={client.blog} limit={6} orders="-publishedAt" href="/blog/{id}">        <article>          <h2><ListItem id="title" /></h2>        </article>        <Pagination />        <Error template />        <Null>No posts have been published yet.</Null>      </List>    </main>  );}

The article inside List repeats for each post, and ListItem shows its title. limit={6} is the number per page, orders="-publishedAt" sorts by newest publish date, and href is where each detail page lives. Pagination handles paging.

Start the dev server and open /blog. If the title of the post you published appears, you are connected. If the server was already running before you added the environment variable, restart it.

4. Build the post detail page

Next, create app/blog/[id]/page.tsx. It passes the post ID from the list link to View and shows the title and body.

tsx
import { client } from "@/lib/client";import { View, ViewItem, Error, Null } from "@genko-me/react/next";
type Props = {  params: Promise<{ id: string }>;  searchParams: Promise<Record<string, string | string[] | undefined>>;};
export default async function BlogDetail({ params, searchParams }: Props) {  const { id } = await params;
  return (    <main>      <View api={client.blog} id={id} searchParams={searchParams}>        <h1><ViewItem id="title" /></h1>        <ViewItem id="body" />        <Error template />        <Null>Post not found.</Null>      </View>    </main>  );}

Open a post from the list and check its title and body. Style headings, spacing, and body formatting with your site's CSS. The options for each component are in the List and View reference.

5. Preview drafts before publishing

In the API settings in the dashboard, set the preview URL in this form. Replace the domain and path with your own site's.

text
https://your-site.example/blog/{id}?draftKey={key}

During development, you can use a local URL that you can reach from your machine. Save a post as a draft and open "Preview" in the editor to see the saved draft on the same detail page.

That is why the detail page passes searchParams to View: it receives this draftKey. With this setup, you do not need a separate Route Handler just for previews.

The preview shows saved content only; unsaved edits are not reflected. Do not share URLs that contain a draft key as public links. For details, see Preview drafts.

6. Connect your production site

Set the GENKO_API_KEY environment variable on your host as well, then deploy. Your local .env.local is not carried over to production. After deploying, check the list, detail pages, and draft preview on the production URL.

This example is the basic setup for showing posts. To finish it as a public site, also handle per-post metadata such as title, description, and OG image, and return a 404 for posts that do not exist. Writing a message in Null does not by itself set an HTTP 404.

When you add or change schema fields, sync the types with this command.

bash
npx @genko-me/cli pull

If nothing shows up

Symptom

What to check

The list is empty

Whether the post was saved as "Published". Drafts do not appear in the regular list.

401 error

The key's validity period, and GENKO_API_KEY in the runtime environment.

404 error

The workspace ID, endpoint, and post ID, and whether the key allows reading blog.

client.blog is not found

Whether you synced blog with the CLI, and whether the generated path matches your import path.

Drafts do not appear in the preview

Whether the draft is saved, whether the URL has draftKey, and whether you pass searchParams to View.

Start by taking one post through publishing, displaying, and previewing a draft. Once that works, adding images and categories makes it easier to tell connection problems apart from styling. If you want to start with a setup that already has screens, try the updates page template.