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.
import { Copy } from 'copy-ink'
export default function Page() {
return (
<main>
<h1><Copy field="heroTitle" /></h1>
<p><Copy field="hero.subtitle" /></p>
</main>
)
}
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 typesscans your JSX and generates autocompletedfieldstrings. - The client never needs repo access. They sign in with GitHub OAuth;
commits are made by a GitHub App installation scoped to
contents: writeon 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
| Piece | What it does |
|---|---|
<Copy> and friends | Render content in Server Components as plain text |
content/*.yml | The content itself, mirroring your App Router segments |
setupCopyInk | Registers your config once per process |
| Route handler | Sign-in, session, content reads and publishing |
| Middleware | Stamps the request path so Server Components know the route |
| Editor | Lazy-loaded bundle, only for a signed-in client |
| Backend | Turns a changeset into a commit — GitHub, or your working tree |
| CLI | check, 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
- Installation — add the package and create a config.
- Quick start — wire a project end to end.
- How it works — the request lifecycle, in one page.