DEV Community

Cover image for A Complete Guide to GitHub Stacked PRs
Shrestha Pandey
Shrestha Pandey

Posted on

A Complete Guide to GitHub Stacked PRs

You know that feeling when you spend three days building a feature. You open a pull request and it's 1,200 lines long. Your teammate sees it, and says, "I'll review this later." That later becomes tomorrow. Tomorrow becomes next week. Meanwhile, main moves on, conflicts appear, and your "small feature" turns into a mini-project.

There's a better way, and it's called stacked PRs. GitHub now supports it natively, in public preview as of July 30, 2026, rolling out to all repositories. This article explains it in plain language.

Current status: Stacked PRs are in public preview. The core feature (creating stacks, reviewing each layer, merging in one click) works today. Merge queue support is rolling out separately over the following weeks, so if your repo relies on a merge queue, double-check compatibility before betting a workflow on it.

The Problem: One Giant PR That No One Wants to Review

Imagine you're adding a new feature: "User preferences."

To make it work, you need to:

  • Add a new database table
  • Create API endpoints
  • Build a settings page in the UI

If you do all of this in one branch and open one PR, you get:

  • A huge diff that's hard to understand
  • Reviewers who don't know where to start
  • Long wait times and painful merge conflicts

This is the "monster PR" problem, which slows everyone down.

The Idea: Break the Feature Into Layers

Instead of one giant change, think in layers:

  • Layer 1 – Database: Add the user_preferences table and migration.
  • Layer 2 – API: Add endpoints to read and update preferences.
  • Layer 3 – UI: Add the settings page that calls those endpoints.

Each layer is small, focused, and easy to review. You still build the full feature, but you split the work into three smaller pull requests that build on top of each other. That's a stack.

Normal vs Stacked Workflow

Each PR's base is the branch below it, not main. GitHub shows this as a stack map at the top of the pull request, so reviewers can see how the change they're looking at fits into the larger work.

Why This Is Worth It

Stacked PRs solve real problems:

  • Faster reviews: A 200-line PR gets reviewed much faster than a 1,200-line PR.
  • Less pain when merging: Smaller PRs mean fewer conflicts and easier rebases.
  • You can ship incrementally: Once the DB PR is approved, you can merge it while still working on the UI.
  • GitHub now supports it natively: As of July 30, 2026 this is built into the pull request workflow itself — no third-party tooling required to get the basic experience. If your team complains about slow reviews or giant PRs, this is a practical fix.

Step-by-Step: Your First Stacked PR

Let's walk through a real example. We'll use the "User preferences" feature with three layers: DB, API, UI.

You only need:

  • Git installed
  • GitHub CLI (gh) installed
  • A repo with stacked PRs enabled (it's rolling out progressively, so check it's available on yours)

Step 1: Install the gh-stack extension

gh extension install github/gh-stack
Enter fullscreen mode Exit fullscreen mode

Check it's installed:

gh stack --help
Enter fullscreen mode Exit fullscreen mode

If you see help text, you're good.

Step 2: Start From main

git checkout main
git pull
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the First Layer (Database)

gh stack init feat/prefs-db
Enter fullscreen mode Exit fullscreen mode

This creates a new branch feat/prefs-db from main and starts a new stack.

Now make your database changes: add the migration file, update models, run tests. Then commit:

git add .
git commit -m "Add user_preferences table and migration"
Enter fullscreen mode Exit fullscreen mode

At this point, you have one branch with one logical change.

Step 4: Add the Second Layer (API)

From the DB branch, add the next layer:

gh stack add feat/prefs-api
Enter fullscreen mode Exit fullscreen mode

This creates feat/prefs-api on top of feat/prefs-db and keeps the stack structure.

Implement the API: add controller/routes, GET /preferences and PUT /preferences, add tests.

Commit:

git add .
git commit -m "Add preferences API endpoints"
Enter fullscreen mode Exit fullscreen mode

Now you have two layers: DB → API.

Step 5: Add the Third Layer (UI)

gh stack add feat/prefs-ui
Enter fullscreen mode Exit fullscreen mode

You're now on feat/prefs-ui, sitting on feat/prefs-api, sitting on feat/prefs-db, sitting on main.

Implement the UI: settings page component, connect to the API, handle loading and error states.

Commit:

git add .
git commit -m "Add user preferences UI"
Enter fullscreen mode Exit fullscreen mode

You now have three layers ready.

Step 6: Push the Whole Stack to GitHub

From the top branch (feat/prefs-ui), run:

gh stack submit
Enter fullscreen mode Exit fullscreen mode

This command pushes all three branches in the right order and creates three pull requests:

  • PR1: feat/prefs-dbmain
  • PR2: feat/prefs-apifeat/prefs-db
  • PR3: feat/prefs-uifeat/prefs-api It links them together as a stack in the GitHub UI. Open any of these PRs and you'll see a stack map showing "this PR is part of a stack" with links to the PRs above and below. Reviewers see the full story, but each PR shows only its own changes.

How Reviewers See It

Without stacked PRs:

one PR, 1,200 lines, touching DB, API, and UI. The reviewer doesn't know where to start and leaves a vague comment: "This is huge, can we split it?"

With stacked PRs:

  • PR1 (DB): 150 lines, just schema and migration. "Is the table correct? Is the migration safe?"
  • PR2 (API): 200 lines, just endpoints. "Are routes correct? Auth in place? Tests passing?"
  • PR3 (UI): 250 lines, just frontend. "Does the UI match the design? Error handling okay?" Each PR is focused. Each review takes 10–15 minutes instead of an hour. That's the whole point.

What Happens When You Need to Change Something?

Say the reviewer asks you to add an index to the user_preferences table (DB layer), plus one more API validation.

Fix the DB branch:

git checkout feat/prefs-db
# edit migration, add index
git add .
git commit -m "Add index on user_id"
Enter fullscreen mode Exit fullscreen mode

Rebase the layers above. Rather than rebasing each branch by hand, use the CLI's built-in cascade rebase, which rebases every branch in the stack onto its updated parent in one step:

gh stack rebase
Enter fullscreen mode Exit fullscreen mode

If a conflict comes up, gh stack rebase walks you through resolving it branch by branch and restores everything to a clean state if you abort.

Update the stack on GitHub:

gh stack push
gh stack submit
Enter fullscreen mode Exit fullscreen mode

gh stack push pushes the rebased branches (force-with-lease where needed), and gh stack submit updates the existing PRs and keeps the stack links intact. Reviewers see your new commits in the right PRs.

Yes, there's rebasing. But:

  • You're rebasing small, focused branches (easier than one giant branch)
  • You do this often, so it becomes routine
  • The CLI's cascade rebase handles the heavy lifting instead of you doing it branch-by-branch

Merging: You Don't Have to Wait for Everything to Be Done

One of the best parts: you can merge layer by layer.

  • PR1 (DB) is approved and CI is green → merge it.
  • PR2 (API) and PR3 (UI) stay open while you keep working.
  • After PR1 merges, the branches above it automatically rebase and retarget onto the updated main.
  • Later, PR2 gets approved → merge. Then PR3 → merge. By default, merging the topmost ready PR in a stack lands that PR and every unmerged layer below it in a single operation — you don't need to enable anything special for this. Your existing branch protections and required checks still govern what actually reaches main.

Real Example: From "Monster PR" to Three Small PRs

Before: One Big PR

Branch: feat/user-preferences

  • Migration + models: ~200 lines
  • API endpoints + tests: ~350 lines
  • UI components + styles + tests: ~650 lines Total: ~1,200 lines in one PR. The reviewer doesn't know where to start, requests changes on tangled code, and keeps pushing the review off. The PR sits for days, conflicts appear, morale drops.

After: Three Stacked PRs

  • PR1 – DB (feat/prefs-db): migration + basic model + validation tests. ~150 lines. Focus: "Is the schema right? Is the migration safe?"
  • PR2 – API (feat/prefs-api): controller + routes, auth checks, input validation, API tests. ~250 lines. Focus: "Are endpoints correct and secure?"
  • PR3 – UI (feat/prefs-ui): settings page, API integration, loading/error states, E2E tests. ~300 lines. Focus: "Does the UX match the spec?" Each reviewer sees a clear, focused job. Reviews happen faster. You get feedback sooner and merge sooner.

Common Questions (Answered Simply)

Isn't this more work?

Upfront, yes — more branches, more PRs. But each PR is easier to create (smaller diff, clearer description), each review is faster, and you spend less time resolving massive merge conflicts later. For anything beyond a tiny change, stacked PRs usually save time overall.

What about CI? Won't tests fail on higher PRs?

Each PR runs CI against its base branch: PR1 (DB) runs on main, PR2 (API) runs on feat/prefs-db, PR3 (UI) runs on feat/prefs-api. Some checks might only fully pass once lower PRs are merged — that's expected. Make sure required checks are green on each PR's base, and note in your PR template that some checks depend on lower layers.

When should I not use stacked PRs?

Avoid them when the change is tiny (a single 100-line PR is fine), when the layers are too tightly coupled to review separately, or when your team is still getting comfortable with basic Git branching and rebasing.

Use them when the feature naturally splits into layers (DB/API/UI, core/extension/integration), reviews are slow because PRs are too big, and your team is comfortable with rebase-based workflows.

Mistakes People Make (and How to Avoid Them)

  1. Too many layers. Don't turn a small feature into 10 tiny PRs. 2–5 PRs per feature is usually right; if you need more, the feature itself may be too big.
  2. Unclear descriptions. Reviewers shouldn't have to guess what a PR does or where it sits in the stack. Say what it does, what it depends on, and what follows it.
  3. Ignoring the bottom PR. The bottom PR is the foundation — if it's messy, everything above it is shaky. Keep it clean, well-tested, and understandable on its own.
  4. Being afraid of rebase. You'll rebase more, but each rebase is smaller, you get good at it fast, and gh stack rebase reduces the manual work significantly. ## A Simple Checklist Before You Push

Before you run gh stack submit, ask:

  • Does each PR have one clear purpose?
  • Can someone understand the bottom PR without seeing the others?
  • Are branch names clear (e.g., prefs-db, prefs-api, prefs-ui)?
  • Did you mention the stack in each PR description?
  • Are tests passing locally on each layer?
  • If your repo uses a merge queue, have you confirmed stacked-PR support is available yet?

If yes, you're ready.

TL;DR (For Your Next Feature)

  • Don't send one giant PR.
  • Split your feature into 2–4 logical layers.
  • Use gh stack (native to GitHub as of the July 2026 public preview) to create a stack of PRs.
  • Let reviewers focus on small, clear diffs.
  • Merge layer by layer, reduce conflicts, and ship faster.

Top comments (0)