How to Fix: Next.js v15 is being installed with ESLint v8
Next.js 15 can unexpectedly pull in ESLint v8 during app creation, even when the ecosystem is moving toward newer linting expectations. The result is a mismatched toolchain: your freshly scaffolded project looks current, but the installed lint setup can lag behind and trigger peer dependency confusion, version warnings, or inconsistent CI behavior.
Understanding the Root Cause
This happens because create-next-app, Next.js 15, and the related eslint-config-next dependency chain do not always resolve to the latest ESLint major automatically during project scaffolding. In practice, the generated app can install a version of ESLint v8 if the package template, lockfile resolution, or peer dependency compatibility range still allows it.
There are three common technical reasons:
- Dependency ranges are permissive. If the scaffolded template or a related package accepts ESLint 8, your package manager may choose that version during resolution.
- Package manager resolution differs. npm, pnpm, and Yarn can resolve peer dependencies differently depending on cache state, lockfiles, and existing global metadata.
- Lint integration is split across packages. next, eslint, and eslint-config-next must stay aligned. If one package is generated with a broader compatibility range than the others, the final install may not match what you expected.
In short, this is usually not a runtime bug in your application code. It is a dependency resolution issue during project initialization.
Step-by-Step Solution
The safest fix is to verify what was installed, then explicitly align your linting dependencies.
1. Reproduce and inspect installed versions
npx create-next-app@latest my-app
cd my-app
npm ls eslint eslint-config-next next
If the output shows eslint@8.x, confirm what your project currently declares in package.json.
2. Update the linting stack explicitly
Install compatible versions of the relevant packages so your project does not rely on implicit resolution.
npm install next@latest react@latest react-dom@latest
npm install -D eslint@latest eslint-config-next@latest
If you use pnpm:
pnpm add next@latest react@latest react-dom@latest
pnpm add -D eslint@latest eslint-config-next@latest
If you use Yarn:
yarn add next@latest react@latest react-dom@latest
yarn add -D eslint@latest eslint-config-next@latest
3. Remove stale lockfile artifacts if resolution stays stuck
If your package manager keeps reinstalling ESLint v8, clear generated artifacts and reinstall cleanly.
rm -rf node_modules package-lock.json
npm install
For pnpm:
rm -rf node_modules pnpm-lock.yaml
pnpm install
For Yarn:
rm -rf node_modules yarn.lock
yarn install
4. Verify the resolved version again
npm ls eslint eslint-config-next next
You want the resolved dependency tree to show the intended ESLint version consistently, without duplicate conflicting majors.
5. Run lint and confirm Next.js integration
npm run lint
If your project does not have a lint script yet, add one to package.json:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
Then run:
npm run lint
6. Pin versions temporarily in team or CI environments
If reproducibility matters, pin your versions instead of depending on broad semver ranges.
{
"devDependencies": {
"eslint": "9.9.0",
"eslint-config-next": "15.0.0"
},
"dependencies": {
"next": "15.0.0",
"react": "18.3.1",
"react-dom": "18.3.1"
}
}
Use the actual currently compatible versions for your setup. The important part is keeping Next.js and its lint config in sync.
7. Use overrides or resolutions only if necessary
When a nested dependency keeps forcing the wrong version, package-manager-level overrides can help.
With npm:
{
"overrides": {
"eslint": "9.9.0"
}
}
With pnpm:
{
"pnpm": {
"overrides": {
"eslint": "9.9.0"
}
}
}
With Yarn classic or Berry:
{
"resolutions": {
"eslint": "9.9.0"
}
}
Use this carefully. Forcing a version that eslint-config-next does not support can produce a different class of failure.
Common Edge Cases
- Old npm version on your machine. Older package managers can resolve peers differently. Check with
npm -vand upgrade if needed. - Existing global cache affects scaffolding. Clear cache if behavior differs across machines.
npm cache verify - Monorepo hoisting hides the real version. In a workspace, the root may hoist ESLint v8 even if the app package expects something else. Inspect both the workspace root and the app directory.
- IDE still uses a stale ESLint binary. Editors like VS Code may cache the previous dependency graph. Restart the editor after reinstalling packages.
- Lockfile committed from another environment. A teammate may have generated the project with a different resolver or package manager version, causing version drift in CI.
- Third-party ESLint plugins are not ESLint 9 ready. Even after upgrading from v8, another plugin may block the migration. Run
npm lson related lint packages and inspect peer warnings carefully.
FAQ
Why does a brand-new Next.js 15 app install ESLint 8 at all?
Because scaffolding does not guarantee the newest major of every transitive tool. It installs versions allowed by the dependency graph at that moment, and that graph may still permit ESLint v8.
Is this a Next.js bug or a package manager bug?
Usually it is a dependency compatibility and resolution issue, not a pure bug in either one alone. create-next-app, eslint-config-next, peer ranges, and your package manager all influence the outcome.
Should I force ESLint 9 immediately?
Only if your full lint toolchain supports it. First verify compatibility between eslint, eslint-config-next, and any extra plugins such as TypeScript, import sorting, or testing-related ESLint packages.
If you want to track the original report and compare behavior against the reproduction repository, review the issue context on the linked reproduction project and validate your installed versions after every clean reinstall.