How to Set Up Prettier and ESLint So They Stop Fighting
You save a file, Prettier adds a trailing comma, ESLint underlines it in red, you run the ESLint fixer, it removes the comma, and on the next save Prettier puts it straight back.

You save a file, Prettier adds a trailing comma, ESLint underlines it in red, you run the ESLint fixer, it removes the comma, and on the next save Prettier puts it straight back. Most teams hit this loop within a week of setting up Prettier and ESLint together, and many give up by disabling one of them. That is a shame, because the two tools are built to work side by side once each is given a clear job.
I have set this up on about a dozen projects, from a small marketing site to a monorepo with forty packages. The fix is the same every time: Prettier owns formatting, ESLint owns code quality, and nothing overlaps. Here is how to get there and keep it that way.
Why Prettier and ESLint Fight in the First Place
ESLint started life doing two jobs. It caught real bugs, such as unused variables or a missing await, and it enforced style rules, such as quote style, semicolons and indentation. Prettier arrived later and does only the second job, but does it far more thoroughly by reprinting your whole file from scratch.
When both tools have opinions about style, they disagree. A shared config like the popular Airbnb or Standard presets turns on dozens of formatting rules in ESLint. Prettier then rewrites the file in its own style, and ESLint reports every difference as an error. Neither tool is wrong. They simply both think they are in charge of the same thing.
Give Each Tool One Job
The cleanest split looks like this:
| Concern | Owned by | Examples |
|---|---|---|
| Whitespace and line breaks | Prettier | Indentation, line length, wrapping long calls |
| Punctuation style | Prettier | Quotes, semicolons, trailing commas |
| Likely bugs | ESLint | Unused variables, unreachable code, missing await |
| Code patterns | ESLint | No var, prefer const, React hook rules |
| Import order | Either, but pick one | Sorted or grouped imports |
Import sorting is the one grey area. A Prettier plugin or an ESLint rule can both do it. Choose one and make sure the other is not configured to touch imports.
A Setup That Works in Five Steps
- Install Prettier as a project dependency, not just as an editor extension. Pin the exact version, because even minor releases occasionally change output, and you want every machine and the CI server to format identically.
- Add a small Prettier config file. Set only the options you genuinely care about, usually print width, single or double quotes, and trailing commas. Every option you leave at the default is one fewer thing to argue about.
- Add eslint-config-prettier as the last entry in your ESLint config. It switches off every ESLint rule that conflicts with Prettier. It must come last so nothing turns those rules back on after it.
- Skip eslint-plugin-prettier unless you have a good reason. It runs Prettier inside ESLint and reports formatting as lint errors, which is slower and fills your editor with red squiggles for things Prettier would have fixed silently.
- Run them separately in scripts: one script to check formatting with Prettier, one to lint with ESLint. In CI, run both and fail the build if either fails.
After this, run Prettier across the whole codebase once in a single commit with a clear message. Put that commit's hash in a blame ignore file so git blame skips over it and still shows who really wrote each line.
The Advice I Disagree With
Many tutorials still recommend eslint-plugin-prettier as the standard way to combine the tools. The Prettier team's own documentation has moved away from recommending it, and in my experience the plugin causes more problems than it solves. On a large file it can make linting several times slower, and it mixes two very different kinds of feedback. A formatting difference is not a bug, and showing it in the same red as an unhandled promise trains people to ignore the red.
I also disagree with the common advice to format on every keystroke or on a short auto-save delay. Format on save is enough. Formatting while you type causes the cursor to jump as lines reflow, and it can fight with the editor's own auto-closing of brackets.
Editor Settings That Prevent Surprises
Most conflicts that survive the setup above come from the editor, not the config. Check these in VS Code or your editor of choice:
- Set Prettier as the default formatter for JavaScript, TypeScript, CSS and JSON, so the built-in formatter does not run instead.
- Turn off any separate ESLint auto-fix on save for style rules. Let ESLint fix only quality issues, or run its fixer by hand.
- Make sure the editor uses the project's Prettier, not a globally installed one. A global version that differs by one release can produce different output.
- Commit an editor config file for line endings and final newlines, so Windows and macOS contributors do not produce noisy diffs.
Catching Problems Before They Reach the Main Branch
A pre-commit hook that runs Prettier and ESLint only on staged files keeps every commit clean without slowing anyone down. Tools like lint-staged handle the "only staged files" part, so a commit touching two files takes a second, not a full project lint.
Keep the CI check as the real gatekeeper, though. Hooks can be skipped, and new contributors sometimes clone without them installed. When something does slip through and breaks a build days later, git bisect can find the commit that broke your build far faster than reading through history by hand.
One more practical point: if your build pipeline minifies code, formatting has no effect on the output at all. What developers see in the browser while debugging depends on source maps, which we cover in why source maps matter.
Frequently Asked Questions
Do I still need ESLint if I use Prettier?
Yes. Prettier never looks at what your code does, only how it is laid out. ESLint catches unused variables, unsafe patterns and framework mistakes that Prettier ignores completely.
Does this setup work with TypeScript?
Yes. Add the TypeScript ESLint parser and its recommended rules, then keep eslint-config-prettier last. Prettier supports TypeScript out of the box.
What about Biome or other all-in-one tools?
Tools that combine formatting and linting avoid the conflict entirely and run much faster. They are a good choice for new projects, but they do not yet cover every ESLint plugin, so check your framework's rules first.
The Short Version to Apply Today
Open your ESLint config, add eslint-config-prettier as the last entry, remove eslint-plugin-prettier if it is there, and set Prettier as the default formatter in your editor. That fixes the save loop on most projects in under ten minutes. For more practical tooling guides, browse our dev tools section.
More in Dev Tools
Dev Tools
How Git Bisect Finds the Commit That Broke Your Build
A bug appears, and you know it worked last week. Somewhere in the hundreds of commits since, one change broke it.
Dev Tools
Why Source Maps Matter More Than Most Developers Realize
A production error log pointing to line 1 of a minified bundle is genuinely useless on its own, a wall of compressed,...
Dev Tools
Browser DevTools Features Most Developers Never Open
Watch most developers debug a page and you will see the same three moves: open the Elements panel, tweak a CSS value, check the...