Programming Beginner 7 min

How to Format JavaScript Code with Prettier and ESLint

Code review comments about formatting — trailing commas, quote style, indentation — are a waste of everyone's time. Automating formatting with Prettier eliminates the entire category of cosmetic review feedback, and ESLint catches the actual bugs and bad patterns that formatting cannot fix.

The common mistake is setting them up in conflict with each other, causing ESLint to flag every line that Prettier already formatted. This guide shows the correct setup: Prettier owns formatting, ESLint owns code quality, and a pre-commit hook ensures nothing unformatted ever reaches the repository.

Step-by-step

  1. 1

    Install Prettier and ESLint

    Install all three tools as development dependencies. @eslint/js provides the modern recommended rule set. eslint-config-prettier is the bridge that prevents formatting conflicts — it turns off all ESLint rules that Prettier handles.

    bash
    npm install --save-dev prettier eslint @eslint/js eslint-config-prettier
    
    # Confirm installations
    npx prettier --version
    npx eslint --version
  2. 2

    Create a .prettierrc Config

    Create .prettierrc in the project root. Prettier has sensible defaults — you only need to override the options you disagree with. The three most commonly customized are semi, singleQuote, and printWidth.

    Pick a config and commit it. The goal is consistency, not perfection. The team's second-choice format, enforced automatically, is better than the best format enforced by social pressure alone.

    json
    // .prettierrc
    {
        "semi": true,
        "singleQuote": true,
        "printWidth": 100,
        "tabWidth": 2,
        "trailingComma": "all",
        "arrowParens": "always"
    }
  3. 3

    Create the ESLint Flat Config

    ESLint v9+ uses a flat config file named eslint.config.js (the old .eslintrc format is deprecated). The key line is eslintConfigPrettier at the end of the array — it disables all ESLint formatting rules so they never conflict with Prettier.

    javascript
    // eslint.config.js
    import js from '@eslint/js';
    import eslintConfigPrettier from 'eslint-config-prettier';
    
    export default [
        js.configs.recommended,
    
        {
            rules: {
                // Your project-specific rules
                'no-console': 'warn',
                'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
                'no-var': 'error',
                'prefer-const': 'error',
            },
        },
    
        // MUST be last — disables ESLint formatting rules
        // so Prettier can own them without conflicts
        eslintConfigPrettier,
    ];
    
    // Note: if your package.json doesn't have "type": "module",
    // use .mjs extension: eslint.config.mjs
  4. 4

    Add Ignore Files

    Tell both tools to skip generated files and dependencies. Without these, Prettier and ESLint waste time processing hundreds of files they should never touch.

    bash
    # .prettierignore
    node_modules/
    dist/
    build/
    coverage/
    *.min.js
    package-lock.json
    pnpm-lock.yaml
    
    # Also add ignores to eslint.config.js:
    # Add this object BEFORE the rules objects:
    {
        ignores: [
            'node_modules/',
            'dist/',
            'build/',
            'coverage/',
            '**/*.min.js',
        ],
    }
  5. 5

    Add Format and Lint Scripts

    Add scripts to package.json so the whole team uses the same commands. format:check is used in CI to fail the build if unformatted code is committed; format is the local fix command.

    javascript
    // package.json
    {
        "scripts": {
            "lint":          "eslint .",
            "lint:fix":      "eslint . --fix",
            "format":        "prettier --write \"**/*.{js,ts,jsx,tsx,json,css,md}\"",
            "format:check":  "prettier --check \"**/*.{js,ts,jsx,tsx,json,css,md}\""
        }
    }
    
    // Usage:
    npm run format        # fix all files in place
    npm run format:check  # just check, no writes (good for CI)
    npm run lint          # report ESLint violations
    npm run lint:fix      # auto-fix fixable violations
  6. 6

    Enable Format on Save in VS Code

    Install the Prettier - Code formatter extension (ID: esbenp.prettier-vscode). Then configure VS Code to format on every save. Commit the settings file so the whole team gets it automatically.

    json
    // .vscode/settings.json
    {
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll.eslint": "explicit"
        },
        // Per-language overrides (if needed)
        "[javascript]": {
            "editor.defaultFormatter": "esbenp.prettier-vscode"
        },
        "[typescript]": {
            "editor.defaultFormatter": "esbenp.prettier-vscode"
        }
    }
  7. 7

    Add a Pre-commit Hook with Husky and lint-staged

    Format on Save is a courtesy to the developer. The pre-commit hook is the enforcement. It runs Prettier and ESLint only on the files staged for the commit — fast, targeted, and automatic. Code that fails the hook cannot be committed.

    bash
    # Install husky and lint-staged
    npm install --save-dev husky lint-staged
    
    # Initialize husky (creates .husky/ directory)
    npx husky init
    
    # The init command creates .husky/pre-commit — replace its content:
    echo 'npx lint-staged' > .husky/pre-commit
  8. 8

    Configure lint-staged

    Add a lint-staged config to package.json. This tells lint-staged exactly which tool to run on which file types. The --fix flag on ESLint means it auto-corrects what it can before the commit; Prettier rewrites the file to match the format config.

    javascript
    // package.json — add this at the top level
    {
        "lint-staged": {
            "*.{js,ts,jsx,tsx}": [
                "prettier --write",
                "eslint --fix"
            ],
            "*.{json,css,md}": [
                "prettier --write"
            ]
        }
    }
    
    // Test it: stage a file with bad formatting, then commit
    git add src/index.js
    git commit -m "test"
    // Prettier runs on src/index.js, ESLint runs on src/index.js
    // If either fails (unfixable ESLint error), the commit is blocked

Tips & gotchas

  • Run <code>npm run format</code> once on the entire codebase before setting up the pre-commit hook — otherwise the first commit after setup rewrites hundreds of files and pollutes the git blame.
  • ESLint and Prettier serve different purposes and should not overlap. ESLint catches <code>no-unused-vars</code>, <code>no-undef</code>, and <code>prefer-const</code>. Prettier handles indentation, quotes, and line length. Never try to enforce formatting through ESLint rules when Prettier is installed.
  • If you are using TypeScript, also install <code>@typescript-eslint/eslint-plugin</code> and <code>@typescript-eslint/parser</code> for type-aware linting rules.
  • Add a CI check (<code>npm run format:check && npm run lint</code>) to your GitHub Actions workflow. This catches anything that slipped past the pre-commit hook (skipped with <code>--no-verify</code>) or was edited directly on the remote.
  • The <code>prettier --write</code> command modifies files in place. If you want to preview changes without writing, use <code>prettier --check</code> — it exits with a non-zero code if any file needs formatting, which is what CI uses.

Wrapping up

Prettier and ESLint together solve two completely different problems. Prettier removes the entire class of formatting debates from your team — no one argues about semicolons when a machine decides. ESLint catches the code patterns that actually matter: undefined variables, unused imports, inconsistent const/let usage. The pre-commit hook is the enforcement layer that means neither tool depends on developer discipline to work.

#Prettier #ESLint #Tooling
Back to all guides

Need Help With Your Project?

Book a free 30-minute consultation to discuss your technical challenges and explore solutions together.