Hi everyone, I've been diving into setting up a new TypeScript monorepo project at work (using Turborepo, if that matters). I'm trying to nail down the developer experience and code consistency from the start.
I've read a ton of articles, but I'm still a bit confused about the practical, day-to-day overlap and distinction between ESLint and Prettier in this specific context. I understand ESLint is for catching bugs and enforcing code quality rules, while Prettier is about formatting style. Yet, for TypeScript, ESLint also seems to handle a lot of stylistic things through plugins like `@typescript-eslint`.
My main questions are:
1. In a TypeScript monorepo, what's the best way to divide responsibilities? Should I try to make ESLint handle *all* code-style rules (like quotes, semicolons) and leave Prettier only for formatting? Or is it better to let Prettier handle the formatting (turning its rules off in ESLint) and use ESLint purely for logic/type issues?
2. I'm worried about rule conflicts and the performance impact of running both tools across multiple packages. Has anyone found a solid, minimal config setup that works well for them without being slow?
3. Are there any specific ESLint plugins or Prettier configurations for TypeScript monorepos that you consider essential?
I'm eager to learn from your setups and any gotchas you've encountered.
I'm a frontend tech lead at a mid-sized e-commerce company (around 50 engineers), and our team maintains several TypeScript monorepos with Turborepo for our main platform. In production, we run both ESLint and Prettier, with ESLint configured to handle code quality and Prettier restricted to formatting.
**Core Comparison**
* **Primary Responsibility & Rule Conflicts:** ESLint is for catching logical errors and enforcing code patterns (e.g., `no-unused-vars`). Prettier is an opinionated code formatter (indentation, line length). The key is to disable *all* formatting rules in ESLint using `eslint-config-prettier`. This prevents the two tools from fighting each other over rules like `semi` or `quotes`. In a TypeScript monorepo, you must extend this config for TypeScript-specific and React-specific plugins.
* **Performance & Cache Impact:** Running ESLint across a large monorepo can be slow, especially with type-aware rules from `@typescript-eslint`. We integrate it with Turborepo's caching; a full lint run on our codebase takes about 90 seconds on a clean CI job, but cached runs are under 10 seconds. Prettier is consistently fast, but you should ignore it in Turborepo's cache as its output is the formatted file itself.
* **Setup & Maintenance Overhead:** A minimal conflict-free setup requires at least four core packages: `eslint`, `prettier`, `@typescript-eslint/eslint-plugin`, and `eslint-config-prettier`. You'll also need a `.prettierrc` (we use a simple one with `singleQuote: true`) and an `.eslintrc.js` that extends the Prettier config last. This adds initial configuration time but eliminates ongoing style debates.
* **Developer Experience Integration:** For day-to-day work, both tools should run in your editor (VS Code) and on pre-commit hooks via `lint-staged` and `husky`. The editor integration is crucial - ESLint provides real-time squiggles for bugs, while Prettier formats on save. We had to configure our VS Code settings to `"editor.formatOnSave": true` and `"editor.codeActionsOnSave": { "source.fixAll.eslint": true }` to make them work together smoothly.
I recommend using **both tools together**, with ESLint focused on logic/type safety and Prettier on formatting. It's the standard setup for a reason. If you were forced to pick only one, I'd need to know your team's tolerance for manual style enforcement and whether you prioritize bug-catching over consistent formatting.
editor is my home
Ah, the annual "which linter/formatter" question. It always comes down to your team's tolerance for bike-shedding.
You asked about letting ESLint handle *all* code-style rules. I tried that last year, using every stylistic rule `@typescript-eslint` offered. It was a disaster. The team spent more time arguing over rule severity - should this be an error or a warning? - than writing code. Prettier's value isn't just formatting, it's in being utterly inflexible. You stop debating and just accept it.
Performance in a monorepo is a real concern. You'll need to run them in parallel, and make sure your ESLint config uses type-aware parsing only where absolutely necessary. It can get glacial.
But really, the minimal config is simple: let Prettier own the whitespace. Use `eslint-config-prettier` to shut ESLint up about it. Use ESLint for what it's actually good at: finding bugs, like floating promises or unused variables. Trying to make one tool do everything is how you end up with a 200-line config file everyone is afraid to touch.