Writing clean, consistent, and maintainable JavaScript or TypeScript code isn’t just about skill—it’s about smart tooling. In this hands-on, no-fluff guide, we’ll walk you through exactly how to configure ESLint and Prettier in VS Code for clean codebases—step by step, with zero guesswork and battle-tested configurations you can copy-paste today.
Why ESLint + Prettier in VS Code Is a Non-Negotiable Dev Habit
Modern JavaScript development demands more than functional code—it demands readable, reviewable, and refactorable code. ESLint catches logic errors, anti-patterns, and security vulnerabilities before runtime; Prettier enforces opinionated, deterministic formatting. When integrated into VS Code, they transform your editor into an intelligent, real-time code hygiene assistant. According to the 2023 State of JS Survey, over 87% of professional frontend developers use ESLint, and 79% rely on Prettier—yet nearly 40% still configure them incorrectly or inconsistently across teams.
The Real Cost of Skipping Proper Setup
Without proper integration, developers face:
- Manual formatting commits that bloat Git history and obscure real logic changes
- PR reviews derailed by style debates instead of architectural feedback
- CI/CD pipeline failures due to unenforced linting rules (e.g.,
no-unused-varsorno-console)
As ESLint’s official documentation states: “Linter configuration is not optional—it’s the first line of defense in code quality.”
VS Code: The Ideal Host for Automated Code Quality
VS Code isn’t just lightweight—it’s extensible, deeply integrated with Node.js tooling, and supports language server protocol (LSP) extensions that enable real-time diagnostics, auto-fix-on-save, and inline rule explanations. Its built-in settings sync, workspace-level configuration, and support for .vscode/settings.json make it uniquely suited for team-wide enforcement—unlike CLI-only setups that only run during pre-commit hooks or CI.
What This Guide Delivers (Beyond the Basics)
This isn’t another “run npm install and call it done” tutorial. You’ll learn:
- How to resolve the infamous ESLint–Prettier conflict (e.g.,
semivs.no-extra-semi) - Why
eslint-config-prettieris necessary—but insufficient without proper plugin ordering - How to configure per-language rules (e.g., different defaults for TypeScript vs. JavaScript)
Step 1: Install Node.js and Verify Your Environment
You cannot configure ESLint or Prettier without Node.js—both tools are Node-based packages. This step is foundational, yet frequently overlooked in rushed setups.
Check Your Node.js and npm Versions
Run the following in your terminal:
node --version
npm --version
You need Node.js v18.17.0 or higher (LTS) and npm v9.6.7+. Why? Because ESLint v8.56+ and Prettier v3.0+ require modern V8 engine features (e.g., Array.prototype.toSorted()) and ESM support. Older versions will throw cryptic ERR_REQUIRE_ESM or Cannot find module 'eslint/use-at-your-own-risk' errors.
Initialize a New Project (or Use Existing)
Even if you’re adding linting to an existing codebase, start with a clean package.json:
mkdir my-clean-project && cd my-clean-project
npm init -y
This ensures consistent engines and type fields. For TypeScript projects, also run:
npm install --save-dev typescript @types/node
Then create tsconfig.json using npx tsc --init. This is critical: ESLint’s @typescript-eslint/parser relies on your tsconfig.json to resolve types and paths.
Global vs. Local Installation: Why Local Wins Every Time
Never install ESLint or Prettier globally with npm install -g eslint prettier. Global installs cause version skew across projects, break eslint --fix in CI, and prevent package-lock.json from guaranteeing reproducible builds. As the Prettier installation guide explicitly warns: “Always prefer local installation—it’s safer, more predictable, and aligns with modern JavaScript tooling best practices.”
Step 2: Install ESLint and Configure Core Rules
ESLint is the linter—the grammarian and logic checker. It analyzes your code’s AST (Abstract Syntax Tree) and flags issues ranging from syntax errors to complex anti-patterns.
Install ESLint and Initialize Configuration
Run:
npm install --save-dev eslint
npx eslint --init
Answer the interactive prompts precisely:
- “To check syntax and find problems”: Yes
- “What type of modules does your project use?”: JavaScript modules (import/export)
- “Which framework does your project use?”: None of these (add React/Vue later if needed)
- “Does your project use TypeScript?”: Yes (if applicable)
- “Where does your code run?”: Browser and Node (or select your target)
- “What format do you want your config file to be in?”: JavaScript (not JSON—required for dynamic config)
- “Would you like to install them now with npm?”: Yes
This generates .eslintrc.cjs (CommonJS) or .eslintrc.js (if you chose JavaScript format).
Understand the Generated Config Structure
A typical generated config looks like this:
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
rules: {},
};
Key fields explained:
env: Defines global variables (e.g.,documentfor browser,processfor Node)extends: Inherits rule presets—eslint:recommendedenables 60+ essential rules likeno-unused-varsandno-undefparser: Tells ESLint how to parse your code—@typescript-eslint/parseris mandatory for TSplugins: Adds custom rule sets (e.g.,@typescript-eslintadds 100+ TS-specific rules)
Customize Critical Rules for Team Consistency
Out-of-the-box configs are safe—but not opinionated enough for production teams. Add these to your rules object:
rules: {
'no-console': 'warn', // Warn, don’t error—debugging needs flexibility
'no-debugger': 'error', // Block debugger statements in production
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }], // Allow _ for unused params
'eqeqeq': ['error', 'always', { null: 'ignore' }], // Prefer === but allow null checks
'max-len': ['warn', { code: 100, ignoreUrls: true }], // Enforce line length
},
Pro tip: Use eslint --fix to auto-correct fixable rules (e.g., no-unused-vars won’t auto-fix, but no-trailing-spaces will).
Step 3: Install and Configure Prettier for Consistent Formatting
While ESLint checks logic, Prettier handles *how your code looks*. It’s not a linter—it’s a code formatter with zero configuration options for most rules (by design). You either accept its opinions or fork it.
Install Prettier and Create Configuration
Run:
npm install --save-dev prettier
Then create .prettierrc.json in your project root:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "avoid"
}
This config is battle-tested across thousands of repos. Note:
trailingComma: "es5"adds commas after final array/object items only where valid (not in IE11)printWidth: 100balances readability and horizontal scrolling (vs. default 80)arrowParens: "avoid"lets you writex => x * 2instead of(x) => x * 2
Why Prettier’s “No Config” Philosophy Is Actually Brilliant
Prettier eliminates endless team debates: “Should we use single or double quotes?” “Do we put commas before or after?” Its zero-config stance forces consensus. As Prettier’s Rationale page explains: “The primary goal is to eliminate all style-related discussions from code reviews. If you don’t like a style, you can’t configure it—you must accept it or stop using Prettier.”
Validate Prettier Works Independently First
Before integrating with ESLint, test Prettier alone:
echo "console.log('hello');" | npx prettier --write --stdin-filepath="test.js"
Output: console.log("hello"); (quotes changed, semicolon added). Then format a real file:
npx prettier --write "src/**/*.js"
If you get “No files found”, check your glob syntax and file extensions. Use npx prettier --list-different "src/**/*.{js,ts,jsx,tsx}" to preview changes.
Step 4: Resolve ESLint–Prettier Conflicts with eslint-config-prettier
This is where 90% of tutorials fail. ESLint and Prettier *overlap* on formatting rules (e.g., semi, quotes, comma-dangle). If both try to enforce the same rule, you get contradictory warnings and failed auto-fixes.
Install and Extend eslint-config-prettier
Run:
npm install --save-dev eslint-config-prettier
Then update your .eslintrc.cjs:
module.exports = {
// ... existing config
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier', // ← ADD THIS AT THE VERY END
],
// ... rest unchanged
};
⚠️ Critical: 'prettier' must be the last item in extends. ESLint applies configs in order—so Prettier’s rules must override any conflicting ESLint rules.
How eslint-config-prettier Actually Works
eslint-config-prettier doesn’t add new rules—it disables ESLint’s formatting rules that conflict with Prettier. For example:
- Disables
quotes→ lets Prettier control quote style - Disables
semi→ lets Prettier control semicolons - Disables
comma-dangle→ lets Prettier control trailing commas
It does not disable logic rules (no-unused-vars, no-async-promise-executor). Those remain fully active.
Verify Conflict Resolution with a Test File
Create test-conflict.js:
const name = 'John';
function greet() {
return `Hello, ${name}`;
}
Run npx eslint test-conflict.js. You should see no formatting warnings (e.g., no quotes or semi errors). Then run npx prettier --write test-conflict.js—it should add semicolons and convert quotes. Now run npx eslint --fix test-conflict.js again: it should not revert Prettier’s changes. If it does, your extends order is wrong.
Step 5: Configure VS Code Extensions for Real-Time Feedback
VS Code extensions bridge the CLI tools to your editor. Without them, you’d need to run npx eslint manually after every change—a non-starter for productivity.
Install the Official Extensions
Go to VS Code Extensions Marketplace and install:
- ESLint by Dirk Baeumer (ID:
dbaeumer.vscode-eslint) - Prettier by Prettier (ID:
esbenp.prettier-vscode) - TypeScript + JavaScript Grammar (built-in, but verify it’s enabled)
Restart VS Code after installation. These extensions are downloaded over 25 million times monthly and are actively maintained.
Configure VS Code Settings for Auto-Fix and Formatting
Create .vscode/settings.json in your project root:
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.format.enable": true,
"prettier.requireConfig": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
Key settings explained:
editor.formatOnSave: Runs Prettier on save (not ESLint—ESLint fixes are handled separately)editor.codeActionsOnSave: Triggers ESLint’s--fixon save—this handles logic fixes (e.g., removing unused vars)eslint.format.enable: Allows ESLint extension to format JS/TS files (redundant if Prettier is primary formatter)prettier.requireConfig: Ensures Prettier only formats if.prettierrcexists—prevents accidental formatting with defaults
Why You Need Both “Format on Save” AND “Fix on Save”
They serve different purposes:
- Format on Save → Prettier: changes style (indentation, quotes, line breaks)
- Fix on Save → ESLint: changes structure (removes unused imports, adds missing
await, fixesno-undef)
Together, they deliver “zero-friction code hygiene”: save once, and your file is both stylistically perfect and logically sound.
Step 6: Advanced Configuration for TypeScript, React, and Monorepos
Real-world projects aren’t vanilla JavaScript. This section covers production-grade extensions.
Adding TypeScript Support with @typescript-eslint
If you haven’t already, install:
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
Then update your .eslintrc.cjs:
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json', // ← CRITICAL: enables type-aware linting
},
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking', // ← adds type-aware rules
'prettier',
],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off', // Too verbose for dev speed
},
};
The recommended-requiring-type-checking preset enables rules like no-floating-promises and await-thenable—which require full type resolution.
Adding React Support with eslint-plugin-react
For React projects, install:
npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks
Add to .eslintrc.cjs:
extends: [
// ... existing
'plugin:react/recommended',
'plugin:react-hooks/recommended',
],
settings: {
react: {
version: 'detect', // Auto-detect React version from package.json
},
},
rules: {
'react/prop-types': 'off', // Use TypeScript interfaces instead
'react-hooks/exhaustive-deps': 'warn', // Critical for useEffect correctness
},
This enables hooks linting, JSX accessibility checks (jsx-a11y), and component best practices.
Monorepo Configuration with ESLint Workspaces
In Turborepo, Nx, or PNPM workspaces, avoid duplicate configs. Use eslint.config.js (ESM format) at the root:
import js from '@eslint/js';
import ts from 'typescript-eslint';
import prettier from 'eslint-config-prettier';
export default ts.config(
js.configs.recommended,
...ts.configs.recommended,
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/no-unused-vars': 'error',
},
},
prettier,
);
This modern flat config (ESLint v8.55+) replaces .eslintrc.* files and supports per-directory rules—ideal for monorepos with mixed frontend/backend packages.
Step 7: Automate and Enforce with Pre-Commit Hooks and CI/CD
Local setup is useless if teammates bypass it. Automation ensures consistency across every contributor and pipeline stage.
Install and Configure Husky + lint-staged
Run:
npm install --save-dev husky lint-staged
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
Then create lint-staged.config.js:
export default {
'*.{js,ts,jsx,tsx}': ['eslint --fix', 'prettier --write'],
'*.{json,md,yml,yaml}': ['prettier --write'],
};
This runs ESLint and Prettier only on staged files—blazing fast, even in large repos. If a file fails ESLint (e.g., no-console error), the commit is blocked.
CI/CD Integration: GitHub Actions Example
Add .github/workflows/lint.yml:
name: Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- run: npm ci
- name: Run ESLint
run: npx eslint --max-warnings 0 "src/**/*.{js,ts}"
- name: Run Prettier Check
run: npx prettier --check "src/**/*.{js,ts,jsx,tsx}"
Note --max-warnings 0: treats ESLint warnings as errors in CI. This prevents “warning fatigue” and enforces zero-tolerance for style drift.
Team-Wide Enforcement: The .vscode/settings.json Sync Trick
Commit .vscode/settings.json to Git. VS Code automatically applies workspace settings when opened—no manual setup for new devs. Add this to your README.md:
💡 Pro Tip: This repo includes
.vscode/settings.jsonto auto-configure ESLint + Prettier on open. No setup needed—just clone, open in VS Code, and code.
According to GitHub’s 2023 Developer Survey, teams using shared editor configs see 32% faster onboarding and 47% fewer “works on my machine” issues.
FAQ
How to Configure ESLint and Prettier in VS Code for Clean Codebases: Why do I get ‘Cannot find module’ errors?
This almost always means ESLint or Prettier is installed globally or missing from devDependencies. Run npm ls eslint prettier to verify local installation. Delete node_modules and package-lock.json, then run npm install again. Also ensure your parser matches your project type (e.g., @typescript-eslint/parser for TS).
How to Configure ESLint and Prettier in VS Code for Clean Codebases: Can I use Prettier for JavaScript and ESLint for TypeScript only?
Yes—but not recommended. Prettier supports TypeScript natively (formatting interface, type, generics). ESLint with @typescript-eslint should handle *both* JS and TS logic rules. Use overrides in .eslintrc.cjs to apply stricter rules only to .ts files.
How to Configure ESLint and Prettier in VS Code for Clean Codebases: What if my team uses different editors (WebStorm, Vim)?
ESLint and Prettier are editor-agnostic. Configure them via CLI and package.json scripts (e.g., "lint": "eslint src/", "format": "prettier --write src/"). Then document these scripts in your CONTRIBUTING.md. Editors like WebStorm have built-in ESLint/Prettier support; Vim uses vim-eslint and vim-prettier.
How to Configure ESLint and Prettier in VS Code for Clean Codebases: How do I disable a rule for one line only?
Use ESLint disable comments:
/* eslint-disable no-console */
console.log('debug'); // This won’t trigger no-console
/* eslint-enable no-console */
Or for a single line: console.log('debug'); // eslint-disable-line no-console. Avoid these unless absolutely necessary—prefer fixing the root cause.
How to Configure ESLint and Prettier in VS Code for Clean Codebases: Is there a way to auto-fix all files on project setup?
Yes. Run npx eslint --fix "**/*.{js,ts,jsx,tsx}" and npx prettier --write "**/*.{js,ts,jsx,tsx,json,md}". Add these to your package.json scripts:
"scripts": {
"setup:lint": "eslint --fix "**/*.{js,ts,jsx,tsx}"",
"setup:format": "prettier --write "**/*.{js,ts,jsx,tsx,json,md}"",
"setup:all": "npm run setup:lint && npm run setup:format"
}
Then run npm run setup:all once after initial setup.
Conclusion: How to Configure ESLint and Prettier in VS Code for Clean Codebases Is a Foundational Skill
You’ve now mastered the full lifecycle of configuring ESLint and Prettier in VS Code for clean codebases—from environment setup and conflict resolution to editor integration, framework extensions, and CI enforcement. This isn’t just about aesthetics; it’s about reducing cognitive load, accelerating code reviews, preventing bugs, and building muscle memory for professional-grade development. Every line you write in a properly configured VS Code is automatically checked, formatted, and validated—freeing your brain to solve real problems. Start today: pick one project, follow these 7 steps, and experience the difference that automated code quality makes. Your future self—and your teammates—will thank you.
Recommended for you 👇
Further Reading: