ESLint Setup for Next.js
Configure ESLint with the flat config format for a Next.js project using built-in presets.
Search across all documentation pages
Configure ESLint with the flat config format for a Next.js project using built-in presets.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
# Install ESLint (included with create-next-app)
npm install --save-dev eslint eslint-config-next
# Run linting via Next.js CLI
npx next lint
# Lint and auto-fix
npx next lint --fix
# Lint specific directories
npx next lint --dir src --dir appWhen to reach for this: Every Next.js project should have ESLint configured from day one.
// eslint.config.mjs
import { FlatCompat } from "@eslint/eslintrc";
import { dirname } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
rules: {
// Customize rules here
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"react/no-unescaped-entities": "off",
},
},
{
ignores: [
"node_modules/",
".next/",
"out/",
"public/",
"coverage/",
],
},
];
export default eslintConfig;What this demonstrates:
eslint.config.mjsFlatCompat to bridge Next.js configs (which still use the legacy format) into flat confignext/core-web-vitals (includes React, hooks, and import rules) and next/typescriptnpx next lintnext/core-web-vitals preset bundles eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-next, and eslint-plugin-import with strict defaultsnext/typescript preset adds @typescript-eslint/eslint-plugin rules tuned for Next.jseslint.config.mjs) instead of the legacy .eslintrc formatFlatCompat is a bridge that lets you use legacy extends-style configs inside flat config - necessary because Next.js presets haven't fully migrated yetStandalone ESLint (without next lint):
# Run ESLint directly
npx eslint . --fix
# Or via package.json script{
"scripts": {
"lint": "next lint",
"lint:fix": "next lint --fix",
"lint:strict": "next lint --strict"
}
}Strict mode (treat warnings as errors):
npx next lint --strictIgnoring files:
// In eslint.config.mjs - ignores block
{
ignores: [
"**/*.config.js",
"**/*.config.mjs",
"migrations/",
"generated/",
],
}# The next/typescript preset handles these automatically:
# - @typescript-eslint/parser is configured
# - TypeScript-aware rules are enabled
# - .ts and .tsx files are included by defaultThings that will bite you. Each gotcha includes what goes wrong, why it happens, and the fix.
Flat config not detected - If ESLint ignores your eslint.config.mjs, you may have a leftover .eslintrc.* file. ESLint 9 prefers flat config but falls back to legacy if both exist. Fix: Delete all .eslintrc.* files when using flat config.
First run prompts for config - Running npx next lint for the first time shows a setup wizard. Fix: Choose "Strict" for next/core-web-vitals or create the config file manually beforehand.
Ignores must be a separate object - In flat config, the ignores array only works as a global ignore when it is the only key in its config object. Mixing ignores with rules in the same object makes it a filter, not a global ignore. Fix: Always put global ignores in their own { ignores: [...] } object.
Performance on large projects - npx next lint only lints app/, pages/, components/, lib/, and src/ by default. Custom directories need --dir. Fix: Add --dir flags or configure eslint.dirs in next.config.js.
Other ways to solve the same problem - and when each is the better choice.
| Alternative | Use When | Don't Use When |
|---|---|---|
| Biome | You want a single fast tool for linting and formatting | You need the full ESLint plugin ecosystem |
oxlint | You want extremely fast linting written in Rust | You need custom or Next.js-specific rules |
eslint CLI directly | You need full control over which files are linted | You want Next.js-aware defaults out of the box |
next/core-web-vitals, next/typescript) still use the legacy extends format internally.FlatCompat bridges these legacy configs into ESLint 9 flat config.FlatCompat will no longer be needed.next/core-web-vitals bundles React, hooks, import, and Next.js-specific rules with strict defaults.next/typescript adds @typescript-eslint/eslint-plugin rules tuned for Next.js.eslint.config.mjs)..eslintrc.* and eslint.config.mjs exist, ESLint falls back to the legacy file..eslintrc.* files when migrating to flat config to avoid confusion.# Next.js only lints app/, pages/, components/, lib/, src/ by default
npx next lint --dir src --dir utils --dir services"warn" will now fail the lint run.ignores only works as a global ignore when it is the sole key in its config object.ignores with rules in the same object, it acts as a file filter, not a global ignore.{ ignores: [...] } object."@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
]Prefix unused parameters with _ and they will be ignored.
__filename or __dirname globals.fileURLToPath(import.meta.url) and dirname() recreate them.FlatCompat requires baseDirectory to resolve relative config paths.eslint.config.mjs is plain JavaScript, not TypeScript.// @ts-check at the top and use JSDoc annotations for basic type checking.eslint.config.ts with experimental TypeScript config support in ESLint 9.eslint.config.mjs before running npx next lint.next/core-web-vitals defaults.# Direct ESLint (skips Next.js directory defaults)
npx eslint . --fix
# next lint (respects Next.js directory defaults)
npx next lint --fixUse npx eslint . when you need full control over which files are linted.
next/typescript preset configures the TypeScript parser automatically.@/* defined in tsconfig.json are resolved by the parser.Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥