Skip to main content
Technology & EngineeringBuild Tools219 lines

Eslint Prettier

ESLint and Prettier combined setup for JavaScript and TypeScript linting and formatting

Quick Summary31 lines
You are an expert in configuring ESLint and Prettier together for consistent code quality and formatting in JavaScript and TypeScript projects.

## Key Points

- Use `eslint-config-prettier` to disable all ESLint rules that conflict with Prettier. This must come last in the config array.
- Use flat config (`eslint.config.mjs`) for new projects; the legacy `.eslintrc` format is deprecated in ESLint v9.
- Enable `projectService` in `parserOptions` for type-aware rules like `no-floating-promises`. These catch real bugs.
- Use `lint-staged` with Husky to run linting only on staged files, keeping pre-commit hooks fast.
- Prefer `warn` for stylistic rules and `error` for correctness rules to avoid blocking development on non-critical issues.
- Use the underscore pattern (`argsIgnorePattern: '^_'`) for intentionally unused function parameters.
- **Conflicting formatting rules**: If Prettier and ESLint fight over formatting, ensure `eslint-config-prettier` is the last config applied. It disables conflicting ESLint formatting rules.
- **Flat config vs legacy config**: ESLint v9 uses `eslint.config.mjs` by default. Do not mix `.eslintrc.*` files with flat config; only one system is used per project.
- **Missing parser for TypeScript**: If you see parsing errors on `.ts` files, ensure `typescript-eslint` is configured. The default ESLint parser does not understand TypeScript syntax.
- **Prettier version mismatches in teams**: Pin Prettier version exactly and use `.prettierrc` to ensure identical formatting output across all environments.

## Quick Example

```bash
npm install --save-dev eslint @eslint/js typescript-eslint
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
npm install --save-dev globals
```

```
dist
node_modules
coverage
pnpm-lock.yaml
```
skilldb get build-tools-skills/Eslint PrettierFull skill: 219 lines
Paste into your CLAUDE.md or agent config

ESLint + Prettier — Build Tools

You are an expert in configuring ESLint and Prettier together for consistent code quality and formatting in JavaScript and TypeScript projects.

Core Philosophy

Overview

ESLint is the standard JavaScript/TypeScript linter for catching bugs and enforcing code patterns. Prettier is an opinionated code formatter. Used together, ESLint handles code quality rules while Prettier handles formatting. ESLint v9 introduced flat config (eslint.config.mjs) which replaces the legacy .eslintrc format.

Setup & Configuration

Installation (ESLint v9 + Prettier)

npm install --save-dev eslint @eslint/js typescript-eslint
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
npm install --save-dev globals

eslint.config.mjs (Flat Config)

import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettierConfig from 'eslint-config-prettier';
import prettierPlugin from 'eslint-plugin-prettier';
import globals from 'globals';

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,
  prettierConfig,
  {
    plugins: {
      prettier: prettierPlugin,
    },
    rules: {
      'prettier/prettier': 'warn',
      '@typescript-eslint/no-unused-vars': [
        'error',
        { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
      ],
      '@typescript-eslint/no-explicit-any': 'warn',
      '@typescript-eslint/consistent-type-imports': 'error',
      'no-console': ['warn', { allow: ['warn', 'error'] }],
    },
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
      },
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  {
    files: ['**/*.test.ts', '**/*.spec.ts'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      'no-console': 'off',
    },
  },
  {
    ignores: ['dist/', 'node_modules/', 'coverage/', '*.gen.ts'],
  }
);

.prettierrc

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

.prettierignore

dist
node_modules
coverage
pnpm-lock.yaml

package.json Scripts

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "check": "eslint . && prettier --check ."
  }
}

Core Patterns

React Project Additions

npm install --save-dev eslint-plugin-react eslint-plugin-react-hooks
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';

// Add to config array:
{
  plugins: {
    react,
    'react-hooks': reactHooks,
  },
  rules: {
    ...reactHooks.configs.recommended.rules,
    'react/react-in-jsx-scope': 'off',
    'react/prop-types': 'off',
  },
  settings: {
    react: { version: 'detect' },
  },
}

Type-Aware Rules

// In the config object with parserOptions.projectService:
{
  rules: {
    '@typescript-eslint/no-floating-promises': 'error',
    '@typescript-eslint/no-misused-promises': 'error',
    '@typescript-eslint/await-thenable': 'error',
    '@typescript-eslint/require-await': 'error',
  },
}

Lint-Staged + Husky

npm install --save-dev husky lint-staged
npx husky init
// package.json
{
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md,css}": ["prettier --write"]
  }
}
# .husky/pre-commit
npx lint-staged

Inline Directives

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = legacy.getValue();

/* eslint-disable no-console */
console.log('Debug block');
console.log('More debug');
/* eslint-enable no-console */

Best Practices

  • Use eslint-config-prettier to disable all ESLint rules that conflict with Prettier. This must come last in the config array.
  • Use flat config (eslint.config.mjs) for new projects; the legacy .eslintrc format is deprecated in ESLint v9.
  • Enable projectService in parserOptions for type-aware rules like no-floating-promises. These catch real bugs.
  • Use lint-staged with Husky to run linting only on staged files, keeping pre-commit hooks fast.
  • Prefer warn for stylistic rules and error for correctness rules to avoid blocking development on non-critical issues.
  • Use the underscore pattern (argsIgnorePattern: '^_') for intentionally unused function parameters.

Common Pitfalls

  • Conflicting formatting rules: If Prettier and ESLint fight over formatting, ensure eslint-config-prettier is the last config applied. It disables conflicting ESLint formatting rules.
  • Flat config vs legacy config: ESLint v9 uses eslint.config.mjs by default. Do not mix .eslintrc.* files with flat config; only one system is used per project.
  • Type-aware rules are slow: Rules using projectService or project parse your TypeScript project, which adds startup time. This is normal. Use them in CI and lint-staged, not on every keystroke if it feels slow.
  • Missing parser for TypeScript: If you see parsing errors on .ts files, ensure typescript-eslint is configured. The default ESLint parser does not understand TypeScript syntax.
  • Prettier version mismatches in teams: Pin Prettier version exactly and use .prettierrc to ensure identical formatting output across all environments.

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add build-tools-skills

Get CLI access →