Branching & previews
Show reviewers a content change before it reaches production.
You are redesigning the pricing page. The new copy is on a Git branch, but your reviewers do not want to read a diff. They want to click through the real site with the new pricing in place, while visitors keep seeing the current version.
A Graft branch gives you that. It is a separate copy of the content index, built from the files on your Git branch. Your app can read from it instead of the main index, so a preview deployment shows exactly the branch's content. When the change is approved, you merge it.
How Git branches and Graft branches fit together
They do different jobs, and you use both:
- The Git branch holds the files. You edit, commit, and review content there, as usual.
- The Graft branch holds the compiled index for those files, so an app can read and render them.
You create a Graft branch, compile your Git branch's files into it, and point a
preview at it. Merging happens in the same order: merge the Git branch first,
then graft merge the Graft branch. graft merge never touches Git.
Branches need the Postgres engine. On the static engine every Git checkout already compiles its own index file, so there is nothing extra to create.
Create a branch and preview it
Check out your Git branch and create a Graft branch
terminal git switch pricing-redesign graft branch create preview-pricingThe new branch starts empty and copies nothing. Any document you have not changed is read from the main index, which is why creating a branch is instant and dropping one is safe.
Compile your files into the branch
terminal graft compile --branch preview-pricingThis is the same compile you already run, written into the branch instead of
main. Rungraft branch listto see every branch.Pass
branchto any read. Single documents, lists, and search all respect it:app/pricing/page.tsx import { graft } from "@/lib/graft"; const branch = process.env.GRAFT_PREVIEW_BRANCH; // "preview-pricing" on the preview deploy export default async function Pricing() { const page = await graft.getContent("pages", "pricing", { branch }); return <h1>{page?.data.title}</h1>; }On the preview deployment, set
GRAFT_PREVIEW_BRANCH=preview-pricing. In production, leave it unset and reads usemain.
To throw a branch away, drop it. main keeps no trace of it:
graft branch drop preview-pricing
Choose where a branch lives
Graft can store a branch in two ways. Both answer the same commands.
| Overlay (default) | Neon fork | |
|---|---|---|
| How it works | The branch's documents are stored in your existing database, labelled with the branch name | A full copy of the database, made instantly by Neon |
| Cost | Instant, copies nothing | Instant, but needs a Neon project |
| Use it for | Content previews and review | Rehearsing destructive changes, or previews that must not be able to change production |
Create a Neon fork with --backend neon:
graft branch create big-redesign --backend neon
What a branch copies
The two backends differ here, and it matters if production holds personal data.
- Overlay. The branch lives in your existing database, but operational
data (form submissions, orders) is stored per branch, so the branch starts
with none of
main's. - Neon fork. The fork starts as a full copy of the database. Graft then
empties the operational data (
data_records) and the approvals, but nothing else. The audit log, including who did what, and any tables of your own come across as they are.
Treat a Neon fork as holding production data. Give it the same access controls as production, and do not share its credentials more widely.
Merge a branch
-
Merge the Git branch, so the files and any migration files are on
main. -
Run the merge as a dry run, and read what it will change:
graft merge preview-pricing -
Apply it:
graft merge preview-pricing --apply
Applying does three things, in order. It replays any migrations that ran on the branch but not on the target. It moves rows your users created on the branch to the target. Then it recompiles the target from your files.
Keep cached pages fresh
Every compile reports which documents changed. The framework packages turn that
into cache tags, so only the affected pages are refreshed, and a preview branch
never clears main's cache. SDK reference lists
the tag helpers.
Next steps
- Migrations covers schema changes, which merges replay.
- Deploy covers running a preview deployment.
- Operational data explains the user data a branch does not copy.