How to Configure ESLint and Prettier in VS Code for Clean Codebases: 7 Proven Steps to Master Code Quality Instantly

How to Configure ESLint and Prettier in VS Code for Clean Codebases: 7 Proven Steps to Master Code Quality Instantly

A definitive, step-by-step guide on how to configure ESLint and Prettier in VS Code for clean codebases—covering installation, conflict resolution, TypeScript/React support, Husky hooks, and CI/CD enforcement.

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-vars or no-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., semi vs. no-extra-semi)
  • Why eslint-config-prettier is 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., document for browser, process for Node)
  • extends: Inherits rule presets—eslint:recommended enables 60+ essential rules like no-unused-vars and no-undef
  • parser: Tells ESLint how to parse your code—@typescript-eslint/parser is mandatory for TS
  • plugins: Adds custom rule sets (e.g., @typescript-eslint adds 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: 100 balances readability and horizontal scrolling (vs. default 80)
  • arrowParens: "avoid" lets you write x => x * 2 instead 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 --fix on 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 .prettierrc exists—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, fixes no-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.json to 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.


Further Reading: