eslint.config.mjs) with typescript-eslint v8 for linting and Prettier as a separate formatter; connect them via eslint-config-prettier as the last config entry.npx eslint . && npx prettier --check .eslint-config-prettier anywhere except last in the config array.eslint.config.js/.mjs/.cjs) — .eslintrc.* files are deprecated and ignored by defaulteslint-config-prettier MUST be the last item in the config array to correctly disable conflicting formatting ruleseslint-plugin-prettier and manual formatting rules simultaneously — they will conflicttypescript-eslint v8+ is required for ESLint 9 flat config compatibility — v7 does not workeslint-plugin-prettier) — ESLint alone does not format code| Setting | Value | Purpose |
|---|---|---|
| Config file | eslint.config.mjs | Flat config (ESM recommended) |
| TypeScript plugin | typescript-eslint@^8 | Type-aware linting for TS files |
| Prettier bridge | eslint-config-prettier | Disables conflicting ESLint formatting rules |
| Alternative bridge | eslint-plugin-prettier | Runs Prettier as an ESLint rule (slower) |
| Prettier config | .prettierrc | Formatting options |
| Globals package | globals | Provides browser, node globals for flat config |
| Lint command | npx eslint . | Run linting |
| Format command | npx prettier --write . | Format all files |
| Check command | npx prettier --check . | Verify formatting without changes |
| Config validator | npx eslint --print-config file.ts | Debug resolved config for a file |
START
├── Using TypeScript?
│ ├── YES → Install typescript-eslint v8 + @eslint/js
│ └── NO → Install @eslint/js only
├── Want Prettier to run inside ESLint?
│ ├── YES → Use eslint-plugin-prettier/recommended (slower, single command)
│ └── NO → Use eslint-config-prettier (faster, two commands)
├── Using React?
│ ├── YES → Add eslint-plugin-react + eslint-plugin-react-hooks
│ └── NO ↓
├── Need type-aware rules?
│ ├── YES → Use tseslint.configs.recommendedTypeChecked + parserOptions.project
│ └── NO → Use tseslint.configs.recommended (faster)
└── DEFAULT → @eslint/js recommended + eslint-config-prettier
Install ESLint 9, typescript-eslint 8, Prettier 3, and the config bridge. [src1]
npm install --save-dev eslint @eslint/js typescript-eslint eslint-config-prettier prettier
Verify: npx eslint --version → expected: v9.x.x
Create the flat config file in your project root. [src2]
// eslint.config.mjs
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
export default tseslint.config(
{ ignores: ["dist/", "node_modules/", "coverage/"] },
eslint.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.ts", "**/*.tsx"],
rules: {
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/consistent-type-imports": "error",
},
},
eslintConfigPrettier, // MUST be last
);
Verify: npx eslint --print-config src/index.ts | grep semi → no semi rule (disabled by Prettier)
Define formatting preferences in .prettierrc. [src5]
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "lf"
}
Verify: npx prettier --check src/ → All matched files use Prettier code style!
Add convenience scripts to package.json. [src1]
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"check": "eslint . && prettier --check ."
}
}
// eslint.config.mjs — React + TypeScript project
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
import globals from "globals";
export default tseslint.config(
{ ignores: ["dist/", "build/", "node_modules/"] },
eslint.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
plugins: { react: reactPlugin, "react-hooks": reactHooksPlugin },
languageOptions: { globals: { ...globals.browser } },
settings: { react: { version: "detect" } },
rules: {
"react/react-in-jsx-scope": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
},
eslintConfigPrettier,
);
// eslint.config.mjs — Node.js + TypeScript backend
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from "eslint-config-prettier";
import globals from "globals";
export default tseslint.config(
{ ignores: ["dist/", "node_modules/"] },
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
globals: { ...globals.node },
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
},
rules: {
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
},
},
eslintConfigPrettier,
);
// ❌ BAD — Prettier config is overridden by later configs
export default [
eslintConfigPrettier, // Too early!
eslint.configs.recommended, // Re-enables formatting rules
];
// ✅ GOOD — Prettier config last, disables all formatting rules
export default [
eslint.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier, // Last
];
// ❌ BAD — ESLint formatting rules conflict with Prettier
{ rules: { "semi": ["error", "never"], "quotes": ["error", "double"] } }
// ✅ GOOD — Only logic rules in ESLint, formatting in .prettierrc
{ rules: { "no-console": "warn", "prefer-const": "error" } }
eslint.config.*. Fix: rename .eslintrc.*. [src7]npm install globals and add to languageOptions. [src1]@eslint/compat. [src7]typescript-eslint is in the config array. [src2]npx eslint-config-prettier src/index.ts. [src3]projectService: true for faster resolution. [src2]npm ci. [src1]# Check ESLint version
npx eslint --version
# Print resolved config for a specific file
npx eslint --print-config src/index.ts
# Find rules conflicting with Prettier
npx eslint-config-prettier src/index.ts
# Check Prettier version
npx prettier --version
# Test Prettier formatting on a single file
npx prettier --check src/index.ts
| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| ESLint 9.x | Current | Flat config default, .eslintrc deprecated | Use eslint.config.mjs |
| typescript-eslint 8.x | Current | Single package, flat config API | Replace @typescript-eslint/* with typescript-eslint |
| Prettier 3.x | Current | ESM-only, async API | — |
| ESLint 8.x | Maintenance | — | Migrate to 9 |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| JavaScript/TypeScript projects | Using Biome for both linting + formatting | Biome (biomejs.dev) |
| Need granular rule control | Deno projects (built-in linter) | deno lint + deno fmt |
| Team needs enforced code style | Tiny scripts or one-off files | Manual review |
defineConfig and extends were added in ESLint 9.15+ — older 9.x releases lack this convenience APIeslint-plugin-prettier reports formatting issues as ESLint errors, which can be confusingglobals npm package must be installed separately in flat config