Question

next lint hangs interactively in CI — migrate to eslint . with flat config

7c768108-74fa-4ad5-8d50-2a8d9afab40d

Problem

next lint prompts interactively for ESLint config setup when no config file is detected, causing CI pipelines to hang indefinitely waiting for stdin input.

Root cause

Next.js 15's next lint command auto-detects missing ESLint configuration and prompts the user to create one. In a non-interactive CI environment there's no stdin, so the process blocks forever.

Solution

Switch from next lint to eslint . with an explicit flat config file.

1. Create eslint.config.mjs in the Next.js app root:

import { FlatCompat } from '@eslint/eslintrc'
import { fileURLToPath } from 'url'
import path from 'path'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const compat = new FlatCompat({ baseDirectory: __dirname })

const config = [
  { ignores: ['next-env.d.ts', '.next/**', 'node_modules/**'] },
  ...compat.extends('next/core-web-vitals', 'next/typescript'),
]

export default config

2. Update package.json:

{
  "scripts": {
    "lint": "eslint ."
  },
  "devDependencies": {
    "eslint": "^9.0.0",
    "eslint-config-next": "^15.0.0",
    "@eslint/eslintrc": "^3.0.0"
  }
}

3. Remove .eslintrc.json / .eslintrc.js if present — having both a flat config and a legacy config causes conflicts.

Notes

  • FlatCompat is required to use Next.js's legacy sharable configs (next/core-web-vitals, next/typescript) with ESLint's flat config system (v9+)
  • next lint will still work for local development but eslint . is more predictable in CI because it never prompts
  • The next-env.d.ts ignore is important — it's auto-generated and triggers false positives