Skip to content

Monorepos

bun workspaces, pnpm workspaces, and Turborepo all work — here's what to know.

Updated View as Markdown

next-bun-compile handles monorepo layouts automatically. The adapter assembles its traced output tree with the app path preserved (e.g. apps/web/... relative to the repo root), and the compiled binary lands at apps/web/dist/app.

One monorepo-specific requirement: Next.js resolves adapterPath from its own package location, so a nested workspace dependency needs resolving from the app dir:

// apps/web/next.config.ts
import { createRequire } from "node:module";
import type { NextConfig } from "next";

const req = createRequire(process.cwd() + "/");

const nextConfig: NextConfig = {
  adapterPath: req.resolve("next-bun-compile"),
};

export default nextConfig;

Bun workspaces

Standard bun workspaces setup, no special config:

// package.json (monorepo root)
{
  "private": true,
  "workspaces": ["apps/*", "packages/*"]
}
// apps/web/package.json
{
  "name": "web",
  "scripts": {
    "build": "next build"
  },
  "dependencies": {
    "@example/shared": "*",
    "next": "^16.2",
    "next-bun-compile": "^1"
  }
}
bun install                        # at the monorepo root
cd apps/web && bun run build       # produces apps/web/dist/app

A full working monorepo + sharp example is in Recipes › Monorepo + sharp.

pnpm workspaces

Same idea — pnpm’s .pnpm/ hoisted store is handled the same way as bun’s .bun/ store. next-bun-compile walks both.

Turborepo

Works as-is. If you use turbo prune --docker in CI to slim the Docker context, the pruned tree still triggers the nested-layout codepath. next-bun-compile doesn’t care about the workspace tool — only about the standalone layout Next.js produces.

What next-bun-compile does for monorepos

  1. Preserves the nested layout. The adapter’s assembled tree keys files relative to the repo root, so apps/web nesting survives into the binary’s extraction layout.
  2. Merges split node_modules/. Traced outputs can place partial package copies at multiple levels; next-bun-compile merges every location per file so nothing is lost to hoisting.
  3. Workspace packages survive. Anything Next.js’s trace pulled in (including workspace *-version deps) gets embedded from wherever it landed — including plain data files. A route reading ../../packages/shared/fonts/body.ttf through fs works, because the binary mirrors the assembled tree and runs from the app dir inside it, exactly as output: "standalone" does.

Where the runtime tree lands

The extracted tree keeps its workspace path, so an app at apps/web extracts to $NBC_RUNTIME_DIR/apps/web, not $NBC_RUNTIME_DIR. That is what keeps ../../packages/... resolvable at runtime. The build prints it:

next-bun-compile: monorepo layout — runtime tree extracts to <NBC_RUNTIME_DIR>/apps/web

It only matters if you pre-extract with --extract or mount a volume into the tree — see Docker. A single-app repo is unaffected: there is no workspace path, so the tree extracts straight into NBC_RUNTIME_DIR as before.

Common gotchas

“Cannot find module ‘next-bun-compile’”

Next resolves the bare adapterPath string from its own package location, which in a monorepo may not see your app’s nested dependency. Use the createRequire(process.cwd() + "/") pattern shown above.

Mixed package managers

If your CI installs with bun but your repo also has a stale package-lock.json, Next.js’s “multiple lockfiles” warning may confuse workspace detection. Remove the unused lockfile.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close