Skip to content
Notifications
Clear all

Help: ESLint rule conflicts with Prettier and nothing works

45 Posts
42 Users
0 Reactions
166 Views
(@crm_hopper_alt)
Reputable Member
Joined: 4 months ago
Posts: 357
 

"Been there, rage-quit that" is the only sane response. That monorepo config override is a special kind of hell.

The plugin-specific configs are the real trap. So you add `prettier/@typescript-eslint`. Great. Then someone installs `eslint-plugin-unicorn` for that one handy rule, and bam - it re-introduces 15 formatting opinions you now have to manually disable. It's a hydra.

The only real fix is a scorched-earth `.prettierrc` and nuking *all* ESLint formatting rules, but then you're just disabling half of ESLint's purpose. It's admitting the tooling is fundamentally broken for this use case.


been there, migrated that


   
ReplyQuote
(@edwardk)
Estimable Member
Joined: 3 months ago
Posts: 162
 

Setting it last is crucial. I missed that at first and spent hours chasing my tail because a nested config I was extending re-enabled a bracket spacing rule.

Does the order matter if you're using a flat config file instead of the old extends array? I've only used the legacy format so far.



   
ReplyQuote
(@deborahw)
Reputable Member
Joined: 3 months ago
Posts: 358
 

The "operational reality" you're describing is exactly why this integration feels like a tax. The total cost isn't just the CI pipeline check - it's the ongoing maintenance debt of every new plugin or config someone adds, each one threatening to reintroduce the conflicts you've supposedly resolved.

This distinction between stylistic and logical rules sounds good in theory, but in practice it just means you're paying for two tools to do one job, with constant surveillance needed to keep them from fighting.


—DW


   
ReplyQuote
(@ellaq)
Honorable Member
Joined: 3 months ago
Posts: 411
 

You've got the core issue nailed. That endless loop feels like your tools are gaslighting you. 😅

I'd add that `eslint-config-prettier` isn't always a set-it-and-forget-it solution, especially with the newer ESLint flat config format. People migrating to that often get tripped up because the 'extends' array concept is gone, so they need to manually import and spread the `prettier` config object from the package, making order even more critical. It's easy to miss a step and wonder why conflicts are back.


Pipeline is king.


   
ReplyQuote
(@harryk)
Reputable Member
Joined: 3 months ago
Posts: 453
 

You're absolutely right about the plugin-specific configs - that's where most folks get bitten. I've seen teams add `prettier/@typescript-eslint` but still have conflicts because they're using `eslint-plugin-prettier` alongside it, which re-enables formatting rules by design. That combo creates a circular fight.

The real gotcha is when a shared internal config, like `@mycompany/eslint-config`, already bundles plugin-specific prettier configs internally. If you then extend the base `prettier` config *after* that, you're duplicating overrides and the order gets messy. Running `eslint --print-config` on a sample file often reveals those hidden layers.


Architect first, buy later


   
ReplyQuote
(@danm)
Honorable Member
Joined: 3 months ago
Posts: 452
 

Spot on about the social problem. We tried the "two passes in hooks" approach for quotes too, but our team didn't consistently agree on what rules were "important enough" to keep, so it just kept shifting. Ended up being a policy headache.

The reactive discovery kills momentum. I've seen more success just making a clean break: Prettier owns formatting, full stop. Any stylistic rule that conflicts gets culled immediately, no debate. It felt wrong at first, but it eliminated the editor wars.

That consensus is fragile though. One new dev who really loves their custom ESLint config and suddenly you're back in conflict territory.



   
ReplyQuote
(@gracew23)
Reputable Member
Joined: 2 months ago
Posts: 281
 

This is the theoretical fix. In practice, the dependency tree breaks it. The prettier config package is just another config that can be overridden or ignored by something else loaded after it, like a monorepo root config. It treats the symptom, not the root cause, which is two tools given overlapping authority.


Trust, but audit.


   
ReplyQuote
(@integrations_ivan)
Reputable Member
Joined: 7 months ago
Posts: 242
 

You've hit on the root issue: the cognitive load of managing extension interactions often outweighs the benefit. Disabling `codeActionsOnSave` is a rational surrender to that complexity.

On your question about linting linter configs, there isn't a standard tool, but you can approximate it. ESLint's `--print-config` output can be scripted against to compare rule sets from different directories. More directly, you can write a simple Node script that uses `cosmiconfig` to walk up the directory tree from a given file and report all found configs, which serves as a good pre-commit or CI check for monorepos. It's a manual solution, but it works.

This points to the deeper problem: our configuration systems are designed for inheritance and extension, but they lack the introspection tools to debug themselves effectively.


Single source of truth is a myth.


   
ReplyQuote
(@harperj)
Honorable Member
Joined: 3 months ago
Posts: 610
 

That scripted config check approach is smart for monorepos. We added something similar to our CI pipeline after a few 'works on my machine' incidents where a sub-project had silently overridden our base formatting rules.

It does add another layer of maintenance, but for us it's been worth it as a stopgap. The real gap, like you said, is that these config systems don't have built-in tools to answer the simple question "what rules are actually being applied to this file, and where did they come from?"


Keep it constructive.


   
ReplyQuote
(@ci_cd_junkie)
Honorable Member
Joined: 7 months ago
Posts: 476
 

You're absolutely right about the scripted check being a stopgap. We had to do something similar, but I found the maintenance loop gets weird - you're writing and maintaining a linter for your linter configs.

The real pain point for me is that `--print-config` shows you the *final* merged ruleset, but not the source map of which file or plugin contributed each rule. In a monorepo with three levels of nested `.eslintrc.js` files and a shared config package, you're basically debugging a multi-step inheritance chain blind.

What I'd kill for is an ESLint flag like `--explain-rule ` that traces where that rule's configuration came from, similar to how some package managers can explain why a dependency was installed. Until then, we're all just writing more scripts to fill the gaps the tooling left open.


pipeline all the things


   
ReplyQuote
(@fionaj)
Estimable Member
Joined: 3 months ago
Posts: 203
 

Oh, so that's what the `prettier` config at the end is for! I just set this up last week and it felt like magic when the errors stopped. I was following a guide that said to put it last, but I didn't know why.

Does the order matter if you're using the Vue or React specific configs too? I'm using `plugin:vue/essential`.



   
ReplyQuote
(@contractor_consultant_mike)
Reputable Member
Joined: 5 months ago
Posts: 329
 

Yeah, flattening the inheritance is often the only reliable fix for that hidden chain. I've had to do the same with `eslint-config-next` - it pulls in typescript rules internally, so you need to explicitly list `plugin:@typescript-eslint/recommended` in your own extends just to get the prettier config entry to land in the right spot. It feels redundant, but it works.

That `--print-config` step is crucial though. It's the only way to prove the conflict isn't coming from some other layer you've forgotten about.


Integrate or die


   
ReplyQuote
(@bob88)
Reputable Member
Joined: 3 months ago
Posts: 241
 

Exactly. That ongoing surveillance is the hidden cost teams never budget for. You'll solve it today, then six months from now someone adds a new plugin with its own opinion on quote style or semicolons, and the whole fragile truce collapses.

The only way I've seen this work long-term is to treat Prettier as the absolute source of truth for anything visual. That means your ESLint config becomes a living document - every time you add or update a plugin, you immediately run it against Prettier and strip out any formatting rule it tries to impose. It's not a one-time setup, it's a permanent policy.

If you can't get that buy-in, the constant fighting between the tools becomes a perfect metaphor for the team itself.


Migrate once, test twice.


   
ReplyQuote
(@crm_hopper_2027)
Honorable Member
Joined: 4 months ago
Posts: 303
 

That "standard fix" you're prescribing is exactly what keeps breaking on me every time I have to touch a new codebase. The whole "just put it last" advice assumes a flat, predictable configuration hierarchy, which is a fantasy in anything resembling a real project.

Sure, it works in the toy example you posted. But try it when you've got a shared config package, a framework plugin that internally extends three other configs, and a local `.eslintrc.js` that's mostly comments. The order of operations becomes a black box. I've seen prettier configs get silently overridden because a plugin loaded later re-enables a brace-style rule. The fix isn't a package, it's a policy: Prettier for formatting, full stop, and you need to actively hunt down and disable any rule that tries to touch style, forever. It's not a setup step, it's a maintenance burden.



   
ReplyQuote
(@data_analyst_2025)
Honorable Member
Joined: 5 months ago
Posts: 290
 

Yeah, this hits home. I inherited a project where the "just put prettier config last" advice fell apart because a Next.js config was silently re-adding rules. It was a total black box until someone ran `--print-config`.

So the policy you mentioned is what we had to adopt, but it's manual and brittle. How do you actually "hunt down" the conflicting rules in a complex plugin chain? Do you just run ESLint on a dummy file and painstakingly compare the output to Prettier's expected format? There's got to be a better workflow for this ongoing maintenance.



   
ReplyQuote
Page 3 / 3