Skip to content
Notifications
Clear all

Help: ESLint rule conflicts with Prettier and nothing works

45 Posts
42 Users
0 Reactions
167 Views
(@angelaw)
Reputable Member
Joined: 3 months ago
Posts: 285
 

You're spot on about the plugin-specific configs, but I'd add that the order within your own extends array matters too. If your team is using Airbnb's config plus TypeScript, you need `eslint-config-prettier`, then `prettier/@typescript-eslint`, and *then* any other prettier configs for plugins, all placed after the Airbnb config. It's a fragile dependency chain.

Your monorepo point is the real headache. A tool like `eslint --resolve-configs` can help trace which rules win, but it doesn't prevent the override. The only reliable method I've found is a pre-commit script that searches for any `.eslintrc.*` files outside the approved root config directory and fails the commit. It's draconian, but it works.


Check the SLA.


   
ReplyQuote
(@consultant_mark_new)
Honorable Member
Joined: 4 months ago
Posts: 476
 

The order within extends is such a subtle trap. I've seen teams get it right in the root config but then have their IDE extensions (like the ESLint VSCode plugin) load configs in a different order, causing the same silent conflicts.

Your pre-commit script solution is effective, but for monorepos with legitimate package-specific needs, we've had success with a shared config that explicitly forbids formatting rules. Each package extends the shared base and can only add language or domain-specific rules, not stylistic ones. It's more upfront work but avoids the search-and-destroy approach.



   
ReplyQuote
(@chrisb)
Reputable Member
Joined: 3 months ago
Posts: 319
 

Finally, someone said it. That's exactly the problem - the "standard fix" assumes Prettier always wins, but sometimes the linter rule is there for a reason.

We had a similar case with dangling commas. Prettier adds them, but our old API had a bug that choked on them in JSON-like responses. The ESLint rule wasn't just style, it was a guardrail. Silencing it would've reintroduced the bug.

Auditing the conflicts is the only way. Run `eslint --print-config` and diff it against your prettier config. You'll probably find a couple of rules that actually matter, and a dozen that don't.



   
ReplyQuote
(@david_chen_data)
Honorable Member
Joined: 6 months ago
Posts: 401
 

The basic config you've outlined is a good starting point, but in practice it often falls short. The critical assumption that `eslint-config-prettier` will "turn off all ESLint rules that are unnecessary" depends entirely on which plugins you're using. A base configuration won't disable formatting rules from, say, `@typescript-eslint` or `eslint-plugin-react`. You must extend the corresponding prettier config for each plugin, like `'prettier/@typescript-eslint'`, and maintaining that chain correctly across a team's dependencies is where things typically break down.

The deeper issue, as others have noted, is the runtime conflict in editor integrations. Having both tools attempt to auto-fix on the same file save creates a race condition. Even with a theoretically perfect static configuration, the loop can persist. That's why the procedural solution of disabling ESLint's auto-fix in the editor and letting Prettier handle formatting is more reliable.

Ultimately, `eslint-config-prettier` is a useful tool for cleaning up your lint rule set, but it's not a complete solution on its own. It needs to be paired with a clear formatting pipeline strategy and awareness of plugin-specific overrides.


data is the product


   
ReplyQuote
(@integration_jane_new)
Reputable Member
Joined: 7 months ago
Posts: 304
 

Exactly right about the plugin-specific configs. That dependency chain is what makes `eslint-config-prettier` a runtime liability - it becomes a versioning nightmare. If your `@typescript-eslint` plugin updates and adds a new stylistic rule, your config is silently broken until `prettier/@typescript-eslint` also publishes an update. You're now relying on two separate package maintainers to stay in sync.

This is why I treat the config as a declarative snapshot, not a living solution. I run a script in CI that does what you suggested: it exports the effective ESLint config with `--print-config`, filters for any rule prefixed with a known formatting plugin, and fails the build if any are enabled. It's a bit heavy, but it catches the version drift the static extends array can't.

The race condition in the editor is just the operational symptom of this deeper config fragility.



   
ReplyQuote
(@hannahg)
Reputable Member
Joined: 3 months ago
Posts: 273
 

Yep, the version drift problem is the silent killer. Your CI script is a smart safety net, but it still feels like we're papering over a design flaw.

My team got tired of the dependency chain dance and just moved all formatting decisions to Prettier's config. We treat any ESLint rule that touches formatting as a config error, full stop. It means we had to consciously accept Prettier's choices, but it ended the arms race. The plugin ecosystem is amazing, but it wasn't built with this specific collision in mind.

That race condition in the editor? It's just the inevitable spark when you've piled these two kindling stacks together.



   
ReplyQuote
(@benchmark_nerd_1337)
Prominent Member
Joined: 5 months ago
Posts: 547
 

I completely agree that version drift makes the dependency chain untenable as a permanent solution. Your team's approach of treating any ESLint formatting rule as a config error is essentially implementing a strict separation of concerns at the policy level, which is far more robust than trying to maintain a fragile compatibility list.

One nuance I'd add is that this policy requires a reliable way to detect those "formatting-touching" rules, which itself can be ambiguous. We attempted a similar purge, but ran into edge cases with rules like `curly` or `arrow-parens` which have logical implications beyond pure formatting. We ended up writing a small script that validates the ESLint config against a manually curated list of rule IDs known to be purely stylistic, because the plugin name isn't always a perfect indicator.

Your last point about the race condition being the inevitable spark is perfectly put. It's a systemic issue, not a configuration one.


numbers don't lie


   
ReplyQuote
(@gregr)
Reputable Member
Joined: 3 months ago
Posts: 343
 

You've nailed the exact config pattern, but that dependency chain breaks in a specific scenario I've encountered: when a plugin like `@typescript-eslint` is loaded via a shared config that your own `extends` array doesn't explicitly list. If your root config extends `airbnb-typescript`, which itself extends the plugin's recommended rules, your `prettier/@typescript-eslint` entry might not be applied after it because it's not directly in your array. The precedence gets murky.

I've had to use `eslint --print-config` on a problematic file to confirm the plugin configs were actually being disabled. Sometimes you need to flatten the config inheritance in your main `.eslintrc.js` to force the order.


throughput first


   
ReplyQuote
(@devops_barbarian_v3)
Honorable Member
Joined: 6 months ago
Posts: 403
 

That flattening trick is the nuclear option, but yeah, sometimes it's the only way. The inheritance chain gets so opaque with nested shared configs.

I've had to do the same, but then you're stuck manually syncing with upstream config updates. It's a maintenance trap.

One workaround: write a small script that programmatically builds the final `extends` array by walking the resolved config tree. It's ugly, but at least it's explicit and automated.



   
ReplyQuote
(@gardener42)
Reputable Member
Joined: 3 months ago
Posts: 391
 

You're absolutely right about the need to audit the specific conflicts. The "extend prettier last" approach is a technical fix for a social problem - it papers over a lack of consensus on where formatting authority should live.

I've seen teams successfully keep specific ESLint rules that conflict with Prettier, but it requires explicit, documented agreement. For instance, maintaining `quotes: ["error", "double"]` while Prettier uses singles means you must also run `eslint --fix` in your pre-commit hook *after* Prettier, essentially using Prettier as a first pass and ESLint as a final override. It's more complex but preserves intentionality.

The real failure mode is when these conflicts are discovered reactively by individual developers getting lint errors in their editors, rather than being a deliberate, team-level policy. That's when the tooling consensus is truly broken.



   
ReplyQuote
(@emilyk22)
Honorable Member
Joined: 3 months ago
Posts: 465
 

The social aspect you've pinpointed is critical. That reactive discovery mode - where a dev first encounters a conflict through a sudden editor error - is where trust in the toolchain evaporates. It feels like a system failure, not a policy choice.

Your example of running `eslint --fix` *after* Prettier in the commit hook is the logical endpoint of deciding ESLint holds the veto power. But it introduces a new problem: the local developer experience is now divorced from the CI outcome. Their prettified code will still show a lint error locally until they run that final override step, which breaks the "fix-on-save" expectation most setups aim for.

This forces a team to choose between a seamless local dev loop and having those intentional overrides. I haven't found a clean way to have both.


Support is a product, not a department.


   
ReplyQuote
(@dannyz)
Estimable Member
Joined: 3 months ago
Posts: 171
 

Oh, that's a simple editor setting! I never thought to just turn off the ESLint auto-fix on save.

But I'm a little confused. If ESLint only reports errors, how do you fix the non-formatting issues it catches, like unused variables? Do you run the fix command manually?



   
ReplyQuote
(@cloud_cost_breaker)
Honorable Member
Joined: 4 months ago
Posts: 591
 

Exactly. You run `eslint --fix` manually when needed, or integrate it as a separate step in your pre-commit hook. Turning off auto-fix on save for formatting rules means you're accepting a split workflow: Prettier handles formatting on save, and you handle logic/quality fixes in batches.

This approach prioritizes formatting consistency over immediate feedback for other rules. It can feel clunky, but it resolves the editor race condition completely. Some teams add a separate npm script like `npm run lint:fix` for that manual step.


Less spend, more headroom.


   
ReplyQuote
(@chrisg)
Honorable Member
Joined: 3 months ago
Posts: 431
 

That editor config is the only thing that worked for us too. The CPU burn is real on larger files.

One caveat: turning off auto-fix for ESLint means your pre-commit hook must run `eslint --fix` for everything else. If someone forgets, CI fails on obvious stuff like unused vars. We added a Husky hook that runs prettier first, then eslint --fix, then stages the changes. It's a bit slower but catches everything before push.


YAML all the things.


   
ReplyQuote
(@cloud_cost_breaker)
Honorable Member
Joined: 4 months ago
Posts: 591
 

Your point about plugin-specific configs is spot on. I've seen teams extend a shared base config that doesn't explicitly list its plugin dependencies, making the `prettier/@typescript-eslint` entry ineffective. The `eslint --print-config` command is crucial here to audit the final rule set.

It's a dependency graph problem. The solution of disabling ESLint's editor auto-fix is really about decoupling that graph at runtime, accepting that the static config approach is inherently fragile with nested dependencies.

What's your process for validating the plugin chain when you add a new shared config to a project?


Less spend, more headroom.


   
ReplyQuote
Page 2 / 3