Skip to content
ESLint & Prettier — Configuration, Integration & CI Setup

ESLint & Prettier — Configuration, Integration & CI Setup

DodaTech Updated Jun 20, 2026 8 min read

ESLint catches bugs and enforces code quality rules. Prettier formats your code consistently. Together, they automate the two most tedious parts of development: finding errors and fixing style.

What You’ll Learn

In this tutorial, you’ll learn how to configure ESLint with the new flat config system, set up Prettier with .prettierrc, combine them without conflicts, integrate with VS Code for fix-on-save, add pre-commit hooks with husky and lint-staged, configure monorepo setups, and run both tools in CI pipelines.

Why It Matters

Inconsistent code style causes pointless code review debates. Linting errors caught in CI waste developer time. A well-configured ESLint + Prettier setup catches bugs before they reach production and eliminates style discussions from code reviews.

Real-World Use

When you save a file in VS Code and it auto-formats — that’s Prettier running. When you see red squiggles under an unused variable — that’s ESLint. Durga Antivirus Pro uses ESLint to enforce secure coding patterns in its JavaScript components, preventing common vulnerabilities before they ship.

    flowchart LR
  A[Developer Writes Code] --> B{ESLint}
  B -->|Errors| C[Fix Issues]
  B -->|Warning| D[Review]
  B -->|Pass| E{Prettier}
  C --> E
  D --> E
  E -->|Format| F[Formatted Code]
  F --> G[Pre-commit Hook]
  G -->|lint-staged| H[Stage & Commit]
  H --> I[CI Pipeline]
  I --> J{Build Passes?}
  J -->|Yes| K[Deploy]
  J -->|No| L[Fix & Re-commit]
  

ESLint Flat Config

ESLint 9+ uses the flat config system (eslint.config.js) instead of the legacy .eslintrc format.

// eslint.config.js — Flat config (ESLint 9+)
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import reactPlugin from 'eslint-plugin-react';
import globals from 'globals';

export default [
  // 1. Global ignores
  {
    ignores: ['dist/', 'node_modules/', '*.min.js'],
  },

  // 2. JavaScript base config
  js.configs.recommended,

  // 3. TypeScript config
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json',
      },
    },
    plugins: {
      '@typescript-eslint': tsPlugin,
    },
    rules: {
      ...tsPlugin.configs.recommended.rules,
      '@typescript-eslint/no-unused-vars': ['error', {
        argsIgnorePattern: '^_',
      }],
      '@typescript-eslint/explicit-function-return-type': 'warn',
    },
  },

  // 4. React config
  {
    files: ['**/*.jsx', '**/*.tsx'],
    plugins: {
      react: reactPlugin,
    },
    rules: {
      ...reactPlugin.configs.recommended.rules,
      'react/react-in-jsx-scope': 'off', // Not needed in React 18+
    },
    settings: {
      react: { version: 'detect' },
    },
  },

  // 5. Custom rules for all files
  {
    rules: {
      'no-console': ['warn', { allow: ['warn', 'error'] }],
      'no-debugger': 'error',
      'eqeqeq': ['error', 'always'],
      'curly': ['error', 'all'],
    },
  },
];

Key Concepts

  • files: pattern to filter which files the config applies to
  • ignores: global file patterns to skip
  • languageOptions: parser (espree, @typescript-eslint/parser) and ecmaVersion
  • plugins: objects with rule definitions
  • rules: rule configurations ('off', 'warn', 'error')

Running ESLint

# Check for issues
npx eslint src/

# Auto-fix fixable issues
npx eslint src/ --fix

# Output results as JSON
npx eslint src/ --format json

# Exit with non-zero on warnings too
npx eslint src/ --max-warnings 0

Prettier Configuration

Prettier is an opinionated formatter. Create a .prettierrc file in your project root:

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "tabWidth": 2,
  "printWidth": 100,
  "arrowParens": "always",
  "bracketSpacing": true,
  "endOfLine": "lf"
}

Running Prettier

# Check formatting
npx prettier --check src/

# Format all files
npx prettier --write src/

# Check specific file types
npx prettier --check "src/**/*.{js,ts,tsx,json,css,md}"

Prettier vs ESLint Overlap

Prettier and ESLint’s formatting rules overlap. Before Prettier, ESLint rules like indent, max-len, and comma-dangle handled formatting. Prettier now handles all formatting; ESLint should focus on code-quality rules.

Prettier replaces these ESLint rules (among others):

  • indent, max-len, comma-dangle, quotes, semi, space-before-function-paren
  • object-curly-spacing, array-bracket-spacing, computed-property-spacing
  • keyword-spacing, key-spacing, comma-spacing

Avoiding Conflicts

Use eslint-config-prettier to turn off all ESLint rules that conflict with Prettier:

// eslint.config.js with Prettier integration
import js from '@eslint/js';
import prettierConfig from 'eslint-config-prettier';

export default [
  js.configs.recommended,

  // Custom rules
  {
    rules: {
      'no-unused-vars': 'warn',
      'no-console': 'warn',
    },
  },

  // MUST be last — disables conflicting rules
  prettierConfig,
];

Install:

npm install --save-dev eslint-config-prettier

VS Code Integration (Fix-on-Save)

Create .vscode/settings.json in your project:

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Pre-commit Hooks with Husky and lint-staged

# Install
npm install --save-dev husky lint-staged

# Initialize husky
npx husky init

# Add lint-staged config to package.json

In package.json:

{
  "lint-staged": {
    "*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,css,md}": ["prettier --write"]
  }
}

Modify .husky/pre-commit:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

Now every commit auto-formats and lints only staged files — keeping the entire codebase clean without slowing down commits.

Monorepo Configuration

For monorepos (Nx, Turborepo, Lerna) with multiple packages:

// Root eslint.config.js
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';

export default [
  {
    ignores: ['**/dist/', '**/node_modules/'],
  },
  // Base config for all packages
  {
    rules: {
      'no-console': 'warn',
    },
  },
  // Package-specific overrides
  {
    files: ['packages/frontend/**'],
    rules: {
      'no-console': 'error',
    },
  },
  {
    files: ['packages/backend/**'],
    rules: {
      'no-console': 'off',
    },
  },
];

Each package can also have its own .prettierrc if they need different formatting (rare — consistency is better).

CI Integration

GitHub Actions example:

# .github/workflows/lint.yml
name: Lint and Format Check

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci
      - run: npx eslint src/ --max-warnings 0
      - run: npx prettier --check src/

For GitLab CI:

lint:
  stage: test
  script:
    - npm ci
    - npx eslint src/ --max-warnings 0
    - npx prettier --check src/

Common Mistakes

1. Running ESLint and Prettier without conflict resolution

Without eslint-config-prettier, they fight over formatting. ESLint formats one way, Prettier formats another. Always add prettier config as the last entry.

2. Using extends in flat config

Flat config doesn’t support extends. Use array spread (...) or import config objects. Legacy .eslintrc files won’t work with ESLint 9+.

3. Not ignoring build output

Running ESLint on dist/ or node_modules/ wastes time and produces noise. Always add ignores for generated directories.

4. Overriding Prettier’s defaults

Prettier works best with its defaults. Changing printWidth to 120 or tabWidth to 4 is common but reduces consistency with the ecosystem.

5. Running lint-staged without husky

Lint-staged alone doesn’t run automatically. Install husky to create the git hook that triggers lint-staged before each commit.

6. Not handling monorepo overrides

A strict ESLint rule in a library package might break a frontend package. Use per-package overrides in the flat config.

Practice Questions

  1. What does eslint-config-prettier do? It disables all ESLint rules that conflict with Prettier’s formatting, allowing ESLint to focus on code quality while Prettier handles formatting.

  2. How does flat config differ from .eslintrc? Flat config uses an array of config objects in eslint.config.js. There’s no extends — you import and spread configs. It’s cleaner and tree-shakeable.

  3. What is lint-staged and why use it? lint-staged runs linters on only the files staged for commit. It prevents committing dirty code without linting the entire codebase on every commit.

  4. How do you fix formatting on save in VS Code? Set editor.formatOnSave: true and editor.defaultFormatter: "esbenp.prettier-vscode". For ESLint fixes, add source.fixAll.eslint to codeActionsOnSave.

  5. Why should ESLint not handle formatting rules? Prettier is a specialised formatter that handles formatting better. ESLint should focus on code quality (unused vars, security, type checking). Separating concerns reduces config complexity.

Challenge

Set up a new project with ESLint flat config, Prettier, husky, and lint-staged from scratch. Add rules for React and TypeScript. Verify that:

  • ESLint catches no-unused-vars
  • Prettier formats on save
  • git commit runs lint-staged and rejects on ESLint errors

Real-World Task

In an existing project, run npx eslint --format json src/ > eslint-report.json and examine the output. Identify the top 3 most common rule violations. Add corresponding rules to prevent them going forward.

FAQ

Should I use ESLint or Prettier?
Both. ESLint for code quality (bugs, patterns, best practices). Prettier for formatting (spacing, quotes, semicolons). They complement each other.
What is @typescript-eslint?
The official TypeScript plugin for ESLint. It provides TypeScript-aware rules: type checking, no-unused-vars with type info, explicit return types, and banning unsafe patterns like any.
Why is flat config better than .eslintrc?
Flat config is simpler (one file), composable (array of objects), and tree-shakeable (no nesting issues). It’s also the future of ESLint — legacy config is deprecated.
Can Prettier replace ESLint entirely?
No. Prettier handles formatting, not code quality. It won’t catch unused variables, potential bugs, or security issues.
How do I ignore specific files in ESLint?
Use ignores: ['path/to/ignore'] in the flat config, or add /* eslint-disable */ at the top of a file for one-off suppression.

Mini Project: Full Toolchain Setup Script

Create a bash script that bootstraps a new project with:

  1. ESLint + flat config + Prettier + husky + lint-staged
  2. TypeScript and React support
  3. VS Code settings for fix-on-save
  4. CI workflow for GitHub Actions

The script should prompt for project type (React, Node, Library) and set up the appropriate plugins.

What’s Next

Before moving on, you should understand:

  • ESLint flat config structure and how plugins work
  • Prettier configuration and how it differs from ESLint
  • How husky + lint-staged prevent unformatted code from being committed
  • CI integration for linting and formatting

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro