ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-04-26Intermediate

Setting Up a pnpm Monorepo with Antigravity — A Practical Workflow

A step-by-step playbook for spinning up a pnpm monorepo with Antigravity's AI assistance, including the gotchas I hit while shipping real apps.

antigravity435pnpm2monorepo6workflow50

"I just wanted three apps to share a few TypeScript types, and I burned half a day on Yarn workspaces config." If you ship iOS, Android, and web in parallel as a solo developer, that hour-vampire feels familiar. I bounced between Yarn and npm workspaces for a while, but switching to pnpm cut my "dependency mishap" rate by roughly 80%. This guide walks through how I bootstrap a pnpm monorepo with Antigravity assisting on the heavy lifting, including the traps I hit personally so you can sidestep them.

Why I picked pnpm — what tipped me off npm and Yarn

Honestly, the trigger was running out of disk space. With several apps on one machine, node_modules directories pile up into gigabytes fast. pnpm stores every package once in a content-addressable global store and then hardlinks each project's files to it. Five projects on the same React 19.x release? Still one physical copy on disk. You get the SSD savings and faster installs as a package deal.

The bigger win, though, is strict phantom-dependency detection. If you import a package that isn't declared in package.json, pnpm refuses to resolve it. That feels strict the first time it bites, but in my experience it surfaces issues at install time instead of the day you finally cut a release branch — which is the kinder failure mode. I used to find these issues in CI after merging to main; now I find them before the first commit.

There's also a less obvious benefit: pnpm's lockfile diff stays much smaller than Yarn's yarn.lock. Code reviews on dependency upgrades are easier to scan, and conflict resolution is far less painful when two branches both touch dependencies. For a solo developer who reviews their own diffs, that's quietly a quality-of-life upgrade.

Let Antigravity scaffold the repo

Antigravity's Plan Mode is the fastest way to sketch the entire structure in one pass. I usually open with a brief like this:

Project name: my-product
Layout:
- apps/web (Next.js 16 App Router + TypeScript)
- apps/mobile (Expo 54 + React Native 0.81)
- packages/ui (shared UI components)
- packages/config (shared ESLint/TypeScript/Tailwind preset)
- packages/types (DB and API domain types)

Constraints:
- Use pnpm workspaces
- Pin Node 22 LTS and pnpm 10 in engines
- packages/config presets are extended by apps/* and packages/ui
- Make circular references between apps and packages physically impossible

Approve the plan once, then switch to Fast Mode so file generation and edits run in parallel. The plan-then-execute flow matters more than it looks: Plan Mode forces Antigravity to surface assumptions you'd normally only notice halfway through the work, and Fast Mode then keeps you out of approval fatigue. If you want to go deeper on the trade-offs between the two modes, the Plan Mode and Fast Mode practical guide is worth a read.

The minimum root config — pnpm-workspace.yaml and .npmrc

Don't outsource this part entirely. Reviewing it yourself pays off later.

# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"
# .npmrc
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=false
node-linker=isolated
public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*

Keep shamefully-hoist=false. Flipping it to true breaks pnpm's "transparent dependency tree" promise and lets phantom dependencies slip back in. Selectively hoist tooling like ESLint and types via public-hoist-pattern so editor Quick Fix actions still light up. I forgot this once and lost a full day to "the editor flags an error but CI is green."

auto-install-peers=true is the other knob worth understanding. Without it, every Tailwind, Storybook, or testing-library package will spam warnings during install. With it, pnpm pulls in the declared peer ranges automatically, which keeps the install phase clean enough that you'll actually read the warnings that matter.

Stitching packages together with workspace:*

The fiddly part of any monorepo is wiring packages with the workspace:* protocol. Hand-edits invite version drift. Open a package.json and trigger Antigravity's Inline Chat (Cmd+I) with something like "wire packages/ui into apps/web using the workspace protocol." It will rewrite the relevant files for you.

// apps/web/package.json
{
  "name": "@my-product/web",
  "dependencies": {
    "@my-product/ui": "workspace:*",
    "@my-product/types": "workspace:*"
  },
  "devDependencies": {
    "@my-product/config": "workspace:*"
  }
}

When changes span multiple files, lean on Antigravity's multi-file editing capability — it removes a surprising amount of manual coordination. After accepting the AI's diff, run pnpm -w build once to confirm every package still compiles. Catching a broken type on your laptop is much cheaper than catching it during a deploy. I also recommend wiring up a top-level pnpm -w typecheck script that runs tsc --noEmit across every workspace; it's the cheapest insurance policy you can write.

Three traps I actually hit

These aren't in the official docs, but they cost me real hours.

Expo and React Native fight pnpm's symlinks. Metro resolves modules against physical node_modules paths, so the default node-linker=isolated triggers Cannot find module errors all over the place. My fix is a per-app .npmrc at apps/mobile/.npmrc that overrides node-linker=hoisted. Ask Antigravity about the latest Expo + pnpm interplay before you commit — the situation moves fast and the right answer changes with each Expo release.

husky and lint-staged forget the working directory. When lint-staged sits at the repo root, you must pass pnpm --filter @my-product/web lint or the linter no-ops on a clean signature. The pnpm + Husky + lint-staged precommit guide shows the layout that actually runs in CI. The symptom is sneaky: lint-staged reports success because it ran zero matchers, and you only notice when ESLint errors hit production code review.

CI installs are slow without a cache. A naked pnpm install --frozen-lockfile in GitHub Actions is wasteful. Combine pnpm/action-setup with actions/cache keyed on the lockfile to share the pnpm store across runs. My current pipeline went from a 90-second cold install to roughly 12 seconds on cache hits. Cache the store directory itself (the path printed by pnpm store path), not just node_modules, because pnpm restoring from the store is materially faster than re-extracting tarballs.

A daily workflow that compounds

Once the structure is in place, the day-to-day rhythm matters more than the initial scaffold. Two habits make the biggest difference for me.

First, I prefix every Antigravity prompt with the workspace I'm focused on. Saying "in apps/web, refactor the auth provider" produces tighter diffs than "refactor the auth provider," because Antigravity then loads context only from that workspace. Plan Mode actually exposes which files it intends to touch before any edit happens, so a misrouted prompt is easy to catch. The cost of one extra phrase up front is much lower than untangling cross-app changes later.

Second, I treat pnpm -w as my universal entry point. Every common task — build, typecheck, lint, test — has a top-level script that fans out across packages. When Antigravity asks "which command should I run to verify this change?" I always have a one-line answer. This also makes onboarding a contributor or a new agent trivial: read package.json, run pnpm -w build, see green.

A practical example: I run pnpm -w deps (an alias for pnpm list -r --depth 0) every Monday morning to spot version drift between workspaces. When two apps end up pinning slightly different React Native patch versions, that's exactly the moment to nudge Antigravity toward consolidation rather than discovering the mismatch during a build break.

A third habit worth adopting: define a pnpm -w changeset script even before you publish anything. The Changesets workflow keeps a running record of which packages changed and how their versions should bump. Even on a private monorepo where you never release to npm, that history is gold for understanding why a package changed three months later. Antigravity reads the changeset files as part of its repo context, so when you ask it to pick a sensible commit message it will naturally weave in the package-level intent. That alignment between human-authored changesets and AI-authored summaries is the kind of small detail that compounds across a year of work. You also get a no-drama path to actually publishing internal libraries to a private registry the day you decide to.

Wrap-up — what to do today

Have Antigravity draft pnpm-workspace.yaml and .npmrc, then read them yourself. After that, growing the monorepo is just "wire each new dependency with the workspace protocol" on repeat. Once that rhythm clicks, the next move is consolidating your ESLint, Biome, and TypeScript presets into packages/config. The Biome lint and format unification guide is a clean follow-up.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-07-18
After Compose-First: Choosing Which View Screens to Migrate, Ranked by Churn Instead of Count
Google has declared Android development Compose-first. Here is how I rank View-based screens for migration using git history rather than screen counts, with the scoring script I actually ran and the three places partial migration quietly duplicates state.
App Dev2026-07-13
The Gate That Stops Visual Damage Before You Hand Bulk Image Optimization to an Agent
When an agent bulk re-encoded a few hundred wallpaper assets, a handful came back with dulled color. Size-reduction alone cannot catch that. Here is how to design a gate that stops bad conversions before merge using three axes — SSIM, ΔE, and file size — with a checker that runs on Pillow and scikit-image.
App Dev2026-07-09
Deciding When to Stop a Staged Rollout, Before You Have To — Agents Watch, I Halt
Field notes on building a Google Play staged-rollout watcher with Antigravity. Crash rate as a ratio to baseline, delayed ANR evaluation, and an explicit insufficient_data verdict — with the halt action kept in human hands.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →