Enforcing Consistent Code Style Across a Monorepo with Biome
Biome unifies formatting, linting and type-checking across monorepo packages with a single config, replacing scattered tools and reducing CI noise.
01 Aug 2026, 09:42 UTC

Why style drift happens in monorepos
A monorepo can contain dozens of packages, each with its own build tooling, dependency versions, and code style preferences. One package might use Prettier with quoted syntax, another with single quotes, and a third might skip formatting entirely. When CI runs lint or format checks, diffs appear across every commit, and new contributors spend time reconciling inconsistent rules rather than writing feature code.
Formatter tools like Prettier and linting tools like ESLint often live side-by-side, each with its own configuration file and version pin. Maintaining parity becomes a part-time job, and the risk of style drift grows as the workspace expands.
Biome's single-binary approach
Biome is a single binary that combines formatting, linting, and type-checking. Instead of managing separate configs for Prettier and ESLint, you add one biome.json at the root and the tool discovers configs in each workspace package. This design lets you keep a shared baseline while still allowing per-project overrides.
Key terms: formatter re-writes code to match style rules (indent style, line width, quote style); line width sets the maximum characters per line before wrapping; indent style chooses between spaces or tabs. Biome's defaults are opinionated but fully configurable: you can set line width 80 and indent style space in the formatter section.
Config discovery in a workspace
When Biome runs from the monorepo root, it walks the directory tree and looks for biome.json in each package. If a package has its own file, its rules apply locally; otherwise the root config propagates down. This means you can set formatter indentStyle space and line width 80 at the root and every package inherits it, while a package needing a different quote style can override just that section.
Adding Biome to your monorepo
Here is a concrete workflow you can follow. Assume you are at the monorepo root with Node.js packages under packages.
Step 1: Install Biome
You can install Biome globally or use the per-run runner. For a one-off format run, pnpm dlx biome format --write --changed works without a global install. If you prefer a permanent CLI, npm i -g biomejs@latest (or the equivalent for your package manager) adds the binary to your PATH. Permission note: global installs may require elevated rights on some systems; using a local package manager runner avoids this.
Step 2: Create a minimal biome.json
| Key | Value |
|---|---|
| indentStyle | space |
| lineWidth | 80 |
Place this configuration at the monorepo root. It sets two common preferences: two-space indentation and a maximum line length of 80 characters. Adjust these values to match your team's existing style or desired new standard.
Step 3: Run the formatter on changed files
biome format --write --changedRun this command from the monorepo root. The --changed flag tells Biome to only parse files that have been modified since the last git commit, which keeps the operation incremental and fast. The --write flag applies any fixes directly to the files.
Expected checks: After the command finishes, inspect the git diff (git diff) to see exactly what changed. Biome should have updated indentation and wrapped lines to respect the 80-character limit. If a file already conforms, no changes are written—making the command safe for CI integration.
Relevant risk: If your monorepo has a large number of files, the first run (without --changed) will parse everything, which may take noticeable time. Subsequent runs with --changed will only process new or modified files, but the initial pass can be heavy.
Trade-offs and limitations
- Rule-set maturity: Biome's built-in rule collection is growing, but some ESLint plugins lack direct equivalents. Teams that rely on highly specialized linting may keep ESLint for those rules during migration.
- CLI version lockstep: The Biome binary version should match the version recorded in your lockfile (pnpm-lock.yaml, package-lock.json, or yarn.lock). Upgrading to a new minor release can shift formatter defaults, producing unexpected diffs in CI.
- Watch-mode CPU usage: Biome's biome watch re-parses the entire workspace on each file change. In a large monorepo this can cause sustained CPU spikes; consider using file-system watchers at the package level or script-based CI runs instead.
- TypeScript project references: Biome does not yet support TypeScript's projectreferences out of the box. If your monorepo uses per-package tsconfig.json references, you'll need a manual config per package or a wrapper script.
How to verify the result: After running biome format --write --changed, run biome ci to confirm that no lint or formatting errors remain. Additionally, open a formatted file in your editor and confirm the line width and indent style match the config. If you have the biome-lsp VS Code extension enabled, formatting on save will match the CLI output.
Actionable next steps
- Add a biome.json at the monorepo root with your preferred formatter options (e.g., indentStyle space, lineWidth 80).
- Run biome format --write --changed on a feature branch and inspect the git diff to confirm the changes align with your style goals.
- Integrate biome ci into your CI pipeline so every pull request checks formatting before merge.
- (Optional) Install the biome-lsp extension for on-save formatting that stays in sync with the CLI.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.