
You've just upgraded to ESLint 9, excited to use the latest tooling in your Next.js project. You run next lint like you always do, and then—everything breaks. Your terminal fills with errors about "Unknown options" for things like useEslintrc, extensions, and resolvePluginsRelativeTo.
If you're staring at this Next.js ESLint 9 extensions error wondering what went wrong, you're not alone. This is one of the most common issues developers face after upgrading to ESLint 9, and the good news is it's completely fixable.
In this guide, I'll explain exactly why this happens, walk you through the solutions, and help you avoid this problem in future upgrades.
When you run npm run lint or next lint in a Next.js project that uses ESLint 9, you get an error that looks something like this:
❌ What you see:
➜ npm run lint
> lint
> next lint
Invalid Options:
- Unknown options: useEslintrc, extensions, resolvePluginsRelativeTo, rulePaths, ignorePath, reportUnusedDisableDirectives
- 'extensions' has been removed.
- 'resolvePluginsRelativeTo' has been removed.
- 'ignorePath' has been removed.
- 'rulePaths' has been removed. Please define your rules using plugins.
- 'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.This error is especially frustrating because your ESLint configuration looks perfectly normal. You probably have a standard .eslintrc.json file like this:
{
"extends": "next/core-web-vitals"
}Nothing fancy. Just the basic Next.js recommended setup. So why is it failing?
The issue isn't with your configuration file—it's with how Next.js internally calls ESLint, and ESLint 9 no longer supports those internal calls.
To understand this Next.js ESLint 9 extensions error, you need to know what changed in ESLint 9. ESLint made a massive architectural shift called the "flat config" system.
In ESLint 8, you could:
.eslintrc.json, .eslintrc.js, or .eslintrc.yaml files--ext .js,.jsx,.ts,.tsx on the command lineuseEslintrc option in the Node.js API to control whether config files were loadedThese options were deeply embedded in ESLint's architecture, and tools like Next.js relied on them internally.
ESLint 9 introduces "flat config" as the default. This means:
eslint.config.js (a single JavaScript file).eslintrc.* files are deprecatedHere's the list of options that ESLint 9 removed, which Next.js was using internally:
| Removed Option | What It Did |
|---|---|
useEslintrc | Controlled whether .eslintrc.* files were loaded |
extensions | Specified which file extensions to lint |
resolvePluginsRelativeTo | Set the base path for resolving plugins |
rulePaths | Added custom rule directories |
ignorePath | Specified the ignore file path |
reportUnusedDisableDirectives | Reported unused ESLint disable comments |
When you run next lint, Next.js doesn't just call ESLint directly. It uses a custom wrapper located in packages/next/src/cli/next-lint.ts. This wrapper passes configuration options to ESLint's Node.js API.
The problem? Next.js was passing options like useEslintrc: true and extensions: ['.js', '.jsx', '.ts', '.tsx'] to ESLint. These options worked fine in ESLint 8, but ESLint 9 throws an error because they no longer exist.
This is a documented issue in the Next.js repository (GitHub Issue #64409), and the Vercel team is aware of it.
There are several ways to fix this Next.js ESLint 9 extensions error. Choose the one that best fits your situation.
The simplest and most reliable fix is to downgrade to ESLint 8. This requires minimal changes and gets your project working immediately.
✅ The fix:
# Uninstall ESLint 9
npm uninstall eslint
# Install ESLint 8
npm install eslint@8 --save-devAfter downgrading, verify your package.json shows ESLint 8:
{
"devDependencies": {
"eslint": "^8.57.0",
"eslint-config-next": "14.2.0"
}
}Run next lint again, and it should work:
✅ Expected output:
➜ npm run lint
> lint
> next lint
✔ No ESLint warnings or errorsWhy this works: ESLint 8 still supports all the options that Next.js internally uses. By downgrading, you restore compatibility until Next.js updates their linting logic.
If you want to stay on ESLint 9, you can bypass next lint entirely and run ESLint directly. This requires setting up the flat config system.
Step 1: Create an eslint.config.mjs file in your project root:
// eslint.config.mjs
import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
export default [
...compat.extends("next/core-web-vitals"),
{
ignores: [".next/*", "node_modules/*"],
},
];Step 2: Install the compatibility package:
npm install @eslint/eslintrc --save-devStep 3: Update your package.json script to use the ESLint CLI directly:
{
"scripts": {
"lint": "eslint . --max-warnings 0"
}
}Step 4: Remove or rename your old .eslintrc.json file (ESLint 9's flat config won't use it):
mv .eslintrc.json .eslintrc.json.backupNow npm run lint will use ESLint directly, bypassing Next.js's wrapper entirely.
Trade-offs: This approach works, but you lose some Next.js-specific linting features that next lint provides, such as automatic caching and integration with the build process.
ESLint 9 provides an escape hatch to temporarily use the legacy configuration system:
ESLINT_USE_FLAT_CONFIG=false npm run lintOr add it to your npm script:
{
"scripts": {
"lint": "ESLINT_USE_FLAT_CONFIG=false next lint"
}
}Important: This is a temporary workaround. ESLint has deprecated the legacy config system, and future versions may remove this option entirely. Use this only as a short-term fix while you plan your migration.
The Next.js team is actively working on ESLint 9 support. If you're not in a rush, you can:
Now that you've fixed the immediate problem, let's talk about how to avoid similar issues in the future.
Before upgrading any major dependency in your Next.js project, check compatibility with eslint-config-next:
# Check the current version of eslint-config-next
npm list eslint-config-next
# Check which ESLint versions it supports
npm info eslint-config-next peerDependenciesAs of early 2024, eslint-config-next still lists ESLint 8 as a peer dependency. Upgrading ESLint beyond what's officially supported is asking for trouble.
Instead of using caret (^) or tilde (~) version ranges, consider pinning major dependencies:
{
"devDependencies": {
"eslint": "8.57.0",
"eslint-config-next": "14.2.0"
}
}This prevents accidental upgrades when running npm install on a fresh clone.
ESLint's migration guide (eslint.org/docs/latest/use/migrate-to-9.0.0) clearly documents all breaking changes. Spending 10 minutes reading changelogs can save hours of debugging.
Before upgrading ESLint in your main project, test the upgrade in a branch or a minimal reproduction:
# Create a test branch
git checkout -b test/eslint-9-upgrade
# Attempt the upgrade
npm install eslint@9 --save-dev
# Run linting
npm run lint
# If it fails, you haven't affected your main branch
git checkout mainMajor Next.js releases often include ESLint-related updates. Check the release notes at nextjs.org/blog before upgrading to understand what's changed.
If you're curious about ESLint's flat config system, here's a quick primer. Understanding this will help you when Next.js eventually requires ESLint 9.
The flat config is a single eslint.config.js (or .mjs, .cjs) file that exports an array of configuration objects:
// eslint.config.js
export default [
{
files: ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"],
rules: {
"no-unused-vars": "error",
},
},
{
files: ["**/*.test.js"],
rules: {
"no-unused-vars": "off", // More lenient for tests
},
},
];Key differences from the old system:
.eslintrc in every directory)extends is replaced by spreading arraysnpm install eslint@8 --save-devpackage.json to prevent accidental upgradesnpm run lint before committing changesThe Next.js ESLint 9 extensions error is frustrating, but it's a temporary pain point in the JavaScript ecosystem's evolution. ESLint's flat config is genuinely better architecture, and once the ecosystem catches up, these issues will be behind us.
For now, stick with ESLint 8, and you'll be fine.
Content creator and developer at UICraft Marketplace, sharing insights and tutorials on modern web development.
Save hours of development time with our premium Next.js templates. Built with Next.js 16, React 19, and Tailwind CSS 4.
Get the latest articles, tutorials, and product updates delivered to your inbox.

Getting "TypeError: Cannot read properties of null (reading 'auth')" when deploying to Vercel? This guide explains why your \_not-found page fails during prerendering and shows you exactly how to fix auth context issues with safe patterns.

The "Invalid Unicode Code Point" Terser error during Next.js builds is a frustrating Docker-specific issue. This guide explains why Alpine Linux triggers this minification failure and provides proven solutions to get your builds working.