Branching & previews

Show reviewers a content change before it reaches production.

Deploy & operate4 min read

needs Postgres

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

  1. Check out your Git branch and create a Graft branch

    terminal
    git switch pricing-redesign
    graft branch create preview-pricing

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

  2. Compile your files into the branch

    terminal
    graft compile --branch preview-pricing

    This is the same compile you already run, written into the branch instead of main. Run graft branch list to see every branch.

  3. Read the branch from your app

    Pass branch to 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 use main.

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 worksThe branch's documents are stored in your existing database, labelled with the branch nameA full copy of the database, made instantly by Neon
CostInstant, copies nothingInstant, but needs a Neon project
Use it forContent previews and reviewRehearsing 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

  1. Merge the Git branch, so the files and any migration files are on main.

  2. Run the merge as a dry run, and read what it will change:

    graft merge preview-pricing
  3. 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.