How to Set Up ESLint and Prettier Together in VS Code Without Conflicts
Almost every JavaScript or TypeScript project ends up with the same two tools: ESLint for code quality and Prettier for formatting. And almost every project ends up with the same headache: you save a file, Prettier reformats it, then ESLint immediately underlines the result in red. Or worse, saving twice produces two different results. Using Prettier and ESLint for JavaScript formatting tackles the same question from another angle. Most tutorials stop at “install both extensions”. This guide goes further: it shows you the exact eslint prettier vscode setup that removes the conflict at the source, with the real config files (flat config and legacy .eslintrc), the exact settings.json, and a troubleshooting table for when things still misbehave. TL;DR: the 60-second version Install eslint, prettier and eslint-config-prettier as dev dependencies. Add eslint-config-prettier as the last item in your ESLint config so it turns off every formatting rule ESLint owns. Install the two official VS Code extensions: dbaeumer.vscode-eslint and esbenp.prettier-vscode. Set Prettier as the default formatter, enable editor.formatOnSave, and add “source.fixAll.eslint”: “explicit” to editor.codeActionsOnSave. Commit .vscode/settings.json so the whole team gets identical behaviour. Now here is the same thing, explained properly. Why ESLint and Prettier fight in the first place The two tools overlap on a narrow but very visible surface: stylistic rules. Tool Job Typical output ESLint Finds bugs and enforces code quality rules (unused vars, unsafe types, bad hooks usage, forbidden imports) “‘user’ is assigned a value but never used” Prettier Reprints your code with a consistent style, no opinions about logic Line width, quotes, semicolons, indentation, trailing commas The conflict appears when ESLint also carries formatting rules such as quotes, semi, indent, comma-dangle or max-len. Prettier writes the file one way, ESLint demands another, and your editor becomes a tug of war. The fix is not clever settings. The fix is deleting the overlap. That is exactly what eslint-config-prettier does: it is a config that switches off every ESLint rule that could ever disagree with Prettier. After that, ESLint handles logic, Prettier handles looks, and neither has an opinion on the other’s territory. Ignore the outdated advice you will find elsewhere prettier.eslintIntegration in settings.json: removed years ago, it does nothing today. prettier-eslint based extensions: still exist, but they add a slow extra layer that most projects do not need. Third-party formatters like “Format Code Action”: unnecessary since VS Code got native codeActionsOnSave support. Step 1: Install the packages locally Always install locally in the project, never globally. The VS Code extensions read the versions from your node_modules, which is what keeps your machine and your CI identical. JavaScript project npm install –save-dev eslint @eslint/js prettier eslint-config-prettier TypeScript project npm install –save-dev eslint @eslint/js typescript typescript-eslint prettier eslint-config-prettier Prefer a guided start? npm init @eslint/config@latest scaffolds an ESLint config for you, then you add Prettier on top with the steps below. Step 2: Create the Prettier config Create .prettierrc.json at the root of the project. Even if you keep the defaults, having the file matters: it is the signal that tells the VS Code extension (and your teammates) that this project is Prettier-managed. { "semi": true, "singleQuote": true, "trailingComma": "all", "printWidth": 100, "tabWidth": 2, "arrowParens": "always", "endOfLine": "lf" } Then add .prettierignore: node_modules dist build coverage .next pnpm-lock.yaml package-lock.json Tip: "endOfLine": "lf" saves mixed Windows/macOS teams from a whole category of phantom diffs. Step 3: Wire ESLint to Prettier (the part everyone skips) This is the actual conflict fix. The golden rule: eslint-config-prettier must always be last, so it overrides any formatting rule enabled before it. Modern flat config: eslint.config.js (ESLint 9 and later) Flat config is the default in current ESLint versions. Here is a complete TypeScript-ready file: // eslint.config.js import js from ‘@eslint/js’; import tseslint from ‘typescript-eslint’; import prettier from ‘eslint-config-prettier/flat’; export default tseslint.config( { ignores: [‘dist/**’, ‘build/**’, ‘coverage/**’, ‘node_modules/**’], }, js.configs.recommended, …tseslint.configs.recommended, { rules: { ‘no-unused-vars’: ‘off’, ‘@typescript-eslint/no-unused-vars’: [‘warn’, { argsIgnorePattern: ‘^_’ }], ‘no-console’: [‘warn’, { allow: [‘warn’, ‘error’] }], eqeqeq: [‘error’, ‘smart’], }, }, // MUST stay last: disables every ESLint rule that conflicts with Prettier prettier, ); For a plain JavaScript project, drop the typescript-eslint lines: // eslint.config.js import js from ‘@eslint/js’; import prettier from ‘eslint-config-prettier/flat’; export default [ { ignores: [‘dist/**’, ‘node_modules/**’] }, js.configs.recommended, prettier, ]; Note: if you are on an older eslint-config-prettier release that does not expose the /flat entry point, import it as import prettier from ‘eslint-config-prettier’; and keep it in the same last position. Legacy config: .eslintrc.json (older projects) Plenty of production codebases still run the legacy format. Same principle, "prettier" last in extends: { "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier" ], "env": { "browser": true, "node": true, "es2022": true }, "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "rules": { "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }] } } What about eslint-plugin-prettier? There is a second approach: run Prettier through ESLint with eslint-plugin-prettier, so formatting issues appear as ESLint errors. It works, but it has costs. Approach Pros Cons Best for Separate tools(eslint-config-prettier only) Fastest, clean separation, fewer moving parts, easiest to debug Two commands in CI instead of one Recommended for 95% of projects Prettier as an ESLint rule(eslint-plugin-prettier) One command, formatting errors visible in the lint report Slower on large repos, noisier red squiggles, harder to isolate failures Teams that want a single gate in CI This guide sticks with the separate-tools approach, which is what the Prettier documentation itself recommends. Step 4: Install the right VS Code extensions Only two are needed. Install them from the Extensions panel or from the command line: code –install-extension dbaeumer.vscode-eslint code –install-extension esbenp.prettier-vscode ESLint (dbaeumer.vscode-eslint): shows lint problems inline and applies auto-fixes on save. Prettier – Code formatter (esbenp.prettier-vscode): the formatter itself. Then help your team by committing .vscode/extensions.json: { "recommendations": [ "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ], "unwantedRecommendations": [ "hookyqr.beautify", "rvest.vs-code-prettier-eslint" ] } Step 5: The exact settings.json Create .vscode/settings.json in the repository (not only in your user settings) so every contributor gets the same behaviour. This is the file that makes format-on-save and lint-fix-on-save cooperate:
How to Set Up ESLint and Prettier Together in VS Code Without Conflicts Read More »
