Skip to content
Notifications
Clear all

Guide: setting up automated linting and formatting in a monorepo

1 Posts
1 Users
0 Reactions
27 Views
(@ellaq)
Honorable Member
Joined: 3 months ago
Posts: 411
Topic starter   [#7770]

Alright, so I've spent the last few weeks deep in the weeds trying to get automated linting and formatting working smoothly across our TypeScript/React monorepo (using pnpm workspaces). The goal was a consistent, zero-friction developer experience: you commit, it runs, everything stays clean. But the monorepo aspect adds so many layers—node_modules per package, root configs, overrides, script orchestration. I tried a few combinations and learned a lot.

I wanted to share my findings on the main tool pairings, because choosing the right one isn't just about the linter/formatter itself, but how well it handles the monorepo structure and integrates into the workflow.

**The Core Contenders:**

* **ESLint + Prettier:** The classic combo. ESLint for code quality, Prettier for formatting. The trick is preventing them from fighting. `eslint-config-prettier` is a must. In a monorepo, you need to think about configuration hierarchy. I used an ESLint config at the root that workspaces can extend, but you have to be careful with parser/project resolution for TypeScript.
* **Biome:** This is the new kid that really piqued my interest. It promises to be a single tool replacing ESLint, Prettier, and even some bundler linting. The performance is insane, which is a huge plus in a large monorepo. However, the migration from the established combo is non-trivial, and some plugin ecosystems (like for specific frameworks) aren't as mature yet.
* **Rome:** Basically the predecessor to Biome (the team forked). I'd skip it now in favor of Biome if you're starting fresh.

**The Real Monorepo Challenges & Solutions:**

The tools are only half the battle. The real headache is setup and execution. Here’s what I focused on:

* **Unified vs. Per-Package Config:** Having a single config file at the root with overrides per package type is simpler to maintain, but you need tools that support this pattern well.
* **Running Efficiently:** You don't want to lint *everything* on every commit. `lint-staged` is absolutely essential. Pair it with `husky` for git hooks. In a monorepo, you have to configure `lint-staged` to run the linter only against files in the specific package that changed, which can be tricky.
* **Caching:** This is where Biome shines with its built-in cache. For ESLint, you might need `--cache` flag and potentially more setup to get similar speed benefits across workspaces.
* **Editor Integration:** Everyone needs their editor to respect the rules in real-time. This requires the ESLint/Prettier (or Biome) extensions and pointing them correctly to the monorepo's config, which can break if you're deep in a sub-package.

After all the testing, I landed on **ESLint + Prettier** for now, purely due to the depth of existing rules and plugins we rely on (like for React Query and our CRM data-fetching hooks). But I'm keeping a very close eye on Biome—the performance and simplicity are incredibly tempting for a monorepo context.

What's everyone else using? Have you managed to get Biome to play nicely with a complex, multi-framework monorepo, or is the ESLint/Prettier combo still the safe bet for production stability?

TIL


Pipeline is king.


   
Quote