Skip to main content

Introduction

copy-ink is a git-backed inline CMS for Next.js sites built for non-technical clients.

Your client signs in on the live site, clicks the text they want to change, edits it, and hits Publish. That produces one git commit and triggers your normal deployment. No database, no CMS server, no parallel admin site.

app/page.tsx
import { Copy } from 'copy-ink'

export default function Page() {
return (
<main>
<h1><Copy field="heroTitle" /></h1>
<p><Copy field="hero.subtitle" /></p>
</main>
)
}
content/_index.yml
heroTitle: "Hello!"
hero:
subtitle: "We make things."

What makes it different

Production renders plain text with zero editor JS. <Copy> emits the string and nothing else — no wrapper element, no class, no style, nothing to fight your CSS. The editor is a strictly separate mode: its bundle only loads after a client has signed in, and a visitor's page contains no trace of it. Other git-backed CMSes get close; none of them enforce that boundary as hard.

The rest:

  • Git is the database. Content is YAML in your repo — diffable, reviewable, revertable.
  • Comments survive. Edits round-trip through the YAML Document API, so the explanatory comments you wrote in a content file are still there after your client's first save.
  • Typed fields. copy-ink types scans your JSX and generates autocompleted field strings.
  • The client never needs repo access. They sign in with GitHub OAuth; commits are made by a GitHub App installation scoped to contents: write on one repository.

Who it is for

copy-ink is aimed at the agency-and-client shape of project: you build and own the site, and someone non-technical needs to change the words on it without filing a ticket.

It is a good fit when:

  • The site is a Next.js App Router project you deploy from git.
  • The content is mostly copy — headings, paragraphs, images, a blog or case study collection.
  • Pages are authored by developers, and the client edits what is already there.

It is a poor fit when the client needs to create pages, when several people edit the same page at once, or when content changes must be live in seconds rather than one deployment away.

How it fits together

PieceWhat it does
<Copy> and friendsRender content in Server Components as plain text
content/*.ymlThe content itself, mirroring your App Router segments
setupCopyInkRegisters your config once per process
Route handlerSign-in, session, content reads and publishing
MiddlewareStamps the request path so Server Components know the route
EditorLazy-loaded bundle, only for a signed-in client
BackendTurns a changeset into a commit — GitHub, or your working tree
CLIcheck, i18n, types, prune, migrate, doctor

Requirements

Next.js 15 or later, App Router, React 19, Node.js 20.9 or later. Pages Router is not supported.

A statically exported site (output: 'export') can render content but cannot use the GitHub backend — the OAuth code-for-token exchange needs a route handler.

Next steps