Skip to main content
Next.js provides eslint-config-next, a shareable ESLint configuration that bundles recommended rule-sets for React, React Hooks, and Next.js-specific patterns.
next lint and the eslint option in next.config.js were removed in Next.js 16. Use the ESLint CLI directly.

Configurations

Three configurations are available:
ConfigDescription
eslint-config-nextBase config with Next.js, React, and React Hooks rules
eslint-config-next/core-web-vitalsUpgrades rules that affect Core Web Vitals from warnings to errors (recommended)
eslint-config-next/typescriptAdds TypeScript-specific rules from typescript-eslint

Setup

1

Install ESLint and the config package

pnpm add -D eslint eslint-config-next
2

Create eslint.config.mjs

eslint.config.mjs
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'

const eslintConfig = defineConfig([
  ...nextVitals,
  globalIgnores([
    '.next/**',
    'out/**',
    'build/**',
    'next-env.d.ts',
  ]),
])

export default eslintConfig
3

Run ESLint

npx eslint .

Rules

The @next/eslint-plugin-next package provides the following rules (all enabled in the recommended config):
RuleDescription
@next/next/google-font-displayEnforce font-display behavior with Google Fonts
@next/next/google-font-preconnectEnsure preconnect is used with Google Fonts
@next/next/inline-script-idEnforce id attribute on next/script with inline content
@next/next/next-script-for-gaPrefer next/script for Google Analytics inline scripts
@next/next/no-assign-module-variablePrevent assignment to the module variable
@next/next/no-async-client-componentPrevent async Client Components
@next/next/no-before-interactive-script-outside-documentPrevent beforeInteractive strategy outside _document
@next/next/no-css-tagsPrevent manual <link> stylesheet tags
@next/next/no-document-import-in-pagePrevent next/document imports outside _document
@next/next/no-duplicate-headPrevent duplicate <Head> in _document
@next/next/no-head-elementPrevent usage of raw <head> element
@next/next/no-head-import-in-documentPrevent next/head imports in _document
@next/next/no-html-link-for-pagesPrevent <a> for internal Next.js navigation
@next/next/no-img-elementPrevent raw <img> (use next/image)
@next/next/no-page-custom-fontPrevent page-level custom font loading
@next/next/no-script-component-in-headPrevent next/script inside next/head
@next/next/no-styled-jsx-in-documentPrevent styled-jsx in _document
@next/next/no-sync-scriptsPrevent synchronous scripts
@next/next/no-title-in-document-headPrevent <title> with Head from next/document
@next/next/no-typosPrevent typos in Next.js data-fetching function names
@next/next/no-unwanted-polyfillioPrevent duplicate Polyfill.io polyfills

Examples

With TypeScript

eslint.config.mjs
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'
import nextTs from 'eslint-config-next/typescript'

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),
])

export default eslintConfig

Disabling rules

eslint.config.mjs
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'

const eslintConfig = defineConfig([
  ...nextVitals,
  {
    rules: {
      'react/no-unescaped-entities': 'off',
      '@next/next/no-page-custom-font': 'off',
    },
  },
  globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),
])

export default eslintConfig

With Prettier

Install eslint-config-prettier to prevent conflicts between ESLint formatting rules and Prettier:
pnpm add -D eslint-config-prettier
eslint.config.mjs
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'
import prettier from 'eslint-config-prettier/flat'

const eslintConfig = defineConfig([
  ...nextVitals,
  prettier,
  globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),
])

export default eslintConfig

Monorepo setup

When Next.js is not installed at the repository root, tell the plugin where to find your app:
eslint.config.mjs
import { defineConfig } from 'eslint/config'
import eslintNextPlugin from '@next/eslint-plugin-next'

const eslintConfig = defineConfig([
  {
    files: ['**/*.{js,jsx,ts,tsx}'],
    plugins: { next: eslintNextPlugin },
    settings: {
      next: { rootDir: 'packages/my-app/' },
    },
  },
])

export default eslintConfig

Using the plugin directly

Use @next/eslint-plugin-next directly when you already have conflicting plugins (e.g. airbnb, react-app):
eslint.config.mjs
import { defineConfig } from 'eslint/config'
import nextPlugin from '@next/eslint-plugin-next'

const eslintConfig = defineConfig([
  {
    files: ['**/*.{js,jsx,ts,tsx}'],
    plugins: { '@next/next': nextPlugin },
    rules: { ...nextPlugin.configs.recommended.rules },
  },
])

export default eslintConfig

Lint-staged

Run ESLint only on staged files with lint-staged:
.lintstagedrc.js
const path = require('path')

const buildEslintCommand = (filenames) =>
  `eslint --fix ${filenames.map((f) => `"${path.relative(process.cwd(), f)}"`).join(' ')}`

module.exports = {
  '*.{js,jsx,ts,tsx}': [buildEslintCommand],
}

Version history

VersionChanges
v16.0.0next lint and the eslint config option removed; use ESLint CLI directly

Build docs developers (and LLMs) love