What you'll learn
Quick Answer
Prettier formats code — spacing, quotes, line breaks — with almost no options. ESLint finds problems — unused variables, likely bugs, risky patterns. Use both, and stop ESLint from also enforcing formatting.
They do different jobs
The distinction matters because most setup trouble comes from blurring it.
Prettier is a formatter. It parses your code and reprints it in a consistent style. It does not care what your code does. It is deliberately opinionated with very few options, because the entire value proposition is removing the discussion.
ESLint is a linter. It analyses code for problems — a variable assigned and never used, a missing React hook dependency, an await inside a loop, a comparison that is always true. It finds bugs, not untidiness.
The overlap is that ESLint historically also had formatting rules — indentation, semicolons, quotes. That is where conflicts arise, and it is why those rules are now deprecated in favour of leaving formatting entirely to Prettier.
Prettier setup
npm install --save-dev prettier
// .prettierrc
{
"semi": true,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "all"
}
Those are essentially all the decisions worth making. That is the point — arguing about the rest is what Prettier exists to prevent.
trailingComma: "all" is worth defaulting to: adding an item to a list then changes one line instead of two, which makes diffs and reviews cleaner.
Add a .prettierignore for build output and vendor files, then wire it into your editor with format-on-save. That is the setting that makes it effortless — you stop thinking about formatting entirely, because saving fixes it.
ESLint setup
npm init @eslint/config@latest
Modern ESLint uses a flat config file:
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
'no-unused-vars': 'warn',
'no-console': 'warn',
'eqeqeq': 'error',
},
},
];
Start from the recommended set rather than assembling rules yourself. Add framework plugins as needed — the React hooks plugin in particular catches missing dependencies, which is a real bug class rather than a style preference.
Rules are off, warn or error. Use error for genuine problems and warn for things you intend to address. A configuration where everything is an error and half of them are ignored trains people to ignore all of them.
Making them work together
The classic problem: ESLint wants double quotes, Prettier rewrites to single quotes, ESLint complains again, and saving fights itself.
The fix is to disable ESLint's formatting rules:
npm install --save-dev eslint-config-prettier
Add it last in your config so it switches off every rule that conflicts. Order matters — placed earlier, later entries re-enable the conflicts.
The division of responsibility is then clean: Prettier owns how code looks, ESLint owns whether it is correct.
There is also a plugin that runs Prettier as an ESLint rule. It works, and it makes formatting problems appear as lint errors, which is noisier than useful. Running them separately is simpler.
Enforcing it without nagging people
Tools only help if they actually run. Three layers, in increasing strictness:
Editor — format on save, lint errors inline. Immediate feedback, zero effort, and it covers most cases.
Pre-commit hook — run the formatter and linter on staged files before committing. Fast because it only touches changed files.
CI — the real enforcement:
- run: npx prettier --check .
- run: npx eslint .
Combined with branch protection, badly formatted code cannot merge — see CI/CD with GitHub Actions. This is what makes it stick, because it applies to everyone automatically and removes the social awkwardness of asking a teammate to fix indentation.
One migration note: run Prettier across the whole codebase in a single commit, and record that commit in .git-blame-ignore-revs so it does not obscure the real history of every line.
