ESLint Plugin
Next.js provides an ESLint plugin, @next/eslint-plugin-next, already bundled within the base configuration that makes it possible to catch common issues and problems in a Next.js application.
Setup ESLint
Get linting working quickly with the ESLint CLI (flat config):
-
Install ESLint and the Next.js config:
Terminalpnpm add -D eslint eslint-config-next -
Create
eslint.config.mjswith the Next.js config:eslint.config.mjsimport { defineConfig, globalIgnores } from 'eslint/config' import nextVitals from 'eslint-config-next/core-web-vitals' const eslintConfig = defineConfig([ ...nextVitals, // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: '.next/**', 'out/**', 'build/**', 'next-env.d.ts', ]), ]) export default eslintConfig -
Run ESLint:
Terminalpnpm exec eslint .
Reference
Recommended rule-sets from the following ESLint plugins are all used within eslint-config-next:
Rules
The full set of rules is as follows:
| Enabled in recommended config | Rule | Description |
|---|---|---|
| @next/next/google-font-display | Enforce font-display behavior with Google Fonts. | |
| @next/next/google-font-preconnect | Ensure preconnect is used with Google Fonts. | |
| @next/next/inline-script-id | Enforce id attribute on next/script components with inline content. | |
| @next/next/next-script-for-ga | Prefer next/script component when using the inline script for Google Analytics. | |
| @next/next/no-assign-module-variable | Prevent assignment to the module variable. | |
| @next/next/no-async-client-component | Prevent Client Components from being async functions. | |
| @next/next/no-before-interactive-script-outside-document | Prevent usage of next/script's beforeInteractive strategy outside of pages/_document.js. | |
| @next/next/no-css-tags | Prevent manual stylesheet tags. | |
| @next/next/no-document-import-in-page | Prevent importing next/document outside of pages/_document.js. | |
| @next/next/no-duplicate-head | Prevent duplicate usage of <Head> in pages/_document.js. | |
| @next/next/no-head-element | Prevent usage of <head> element. | |
| @next/next/no-head-import-in-document | Prevent usage of next/head in pages/_document.js. | |
| @next/next/no-html-link-for-pages | Prevent usage of <a> elements to navigate to internal Next.js pages. | |
| @next/next/no-img-element | Prevent usage of <img> element due to slower LCP and higher bandwidth. | |
| @next/next/no-page-custom-font | Prevent page-only custom fonts. | |
| @next/next/no-script-component-in-head | Prevent usage of next/script in next/head component. | |
| @next/next/no-styled-jsx-in-document | Prevent usage of styled-jsx in pages/_document.js. | |
| @next/next/no-sync-scripts | Prevent synchronous scripts. | |
| @next/next/no-title-in-document-head | Prevent usage of <title> with Head component from next/document. | |
| @next/next/no-typos | Prevent common typos in Next.js's data fetching functions | |
| @next/next/no-unwanted-polyfillio | Prevent duplicate polyfills from Polyfill.io. |
We recommend using an appropriate integration to view warnings and errors directly in your code editor during development.
next lint removal
Starting with Next.js 16, next lint is removed.
As part of the removal, the eslint option in your Next config file is no longer needed and can be safely removed.
Examples
Specifying a root directory within a monorepo
If you're using @next/eslint-plugin-next in a project where Next.js isn't installed in your root directory (such as a monorepo), you can tell @next/eslint-plugin-next where to find your Next.js application using the settings property in your eslint.config.mjs:
import { defineConfig } from 'eslint/config'
import eslintNextPlugin from '@next/eslint-plugin-next'
const eslintConfig = defineConfig([
{
plugins: {
next: eslintNextPlugin,
},
settings: {
next: {
rootDir: 'packages/my-app/',
},
},
files: [
// ...files
],
ignores: [
// ...ignores
],
},
])
export default eslintConfigrootDir can be a path (relative or absolute), a glob (i.e. "packages/*/"), or an array of paths and/or globs.
Disabling rules
If you would like to modify or disable any rules provided by the supported plugins (react, react-hooks, next), you can directly change them using the rules property in your 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',
},
},
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
]),
])
export default eslintConfigWith Core Web Vitals
Enable the next/core-web-vitals rule set by extending it in your ESLint config.
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'
const eslintConfig = defineConfig([
...nextVitals,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
]),
])
export default eslintConfignext/core-web-vitals updates @next/eslint-plugin-next to error on a number of rules that are warnings by default if they affect Core Web Vitals.
The
next/core-web-vitalsentry point is automatically included for new applications built with Create Next App.
With TypeScript
In addition to the Next.js ESLint rules, create-next-app --typescript will also add TypeScript-specific lint rules with next/typescript to your config:
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,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
]),
])
export default eslintConfigThose rules are based on plugin:@typescript-eslint/recommended.
See typescript-eslint > Configs for more details.
With Prettier
ESLint also contains code formatting rules, which can conflict with your existing Prettier setup. We recommend including eslint-config-prettier in your ESLint config to make ESLint and Prettier work together.
First, install the dependency:
pnpm add -D eslint-config-prettierThen, add prettier to your existing ESLint config:
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,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
]),
])
export default eslintConfigRunning lint on staged files
If you would like to use ESLint with lint-staged to run the linter on staged git files, add the following to the .lintstagedrc.js file in the root of your project:
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],
}Migrating existing config
If you already have ESLint configured in your application, we recommend extending from this plugin directly instead of including eslint-config-next unless a few conditions are met.
Recommended plugin ruleset
If the following conditions are true:
- You have one or more of the following plugins already installed (either separately or through a different config such as
airbnborreact-app):reactreact-hooksjsx-a11yimport
- You've defined specific
parserOptionsthat are different from how Babel is configured within Next.js (this is not recommended unless you have customized your Babel configuration) - You have
eslint-plugin-importinstalled with Node.js and/or TypeScript resolvers defined to handle imports
Then we recommend either removing these settings if you prefer how these properties have been configured within eslint-config-next or extending directly from the Next.js ESLint plugin instead:
module.exports = {
extends: [
//...
'plugin:@next/next/recommended',
],
}The plugin can be installed normally in your project:
pnpm add -D @next/eslint-plugin-nextThis eliminates the risk of collisions or errors that can occur due to importing the same plugin or parser across multiple configurations.
Additional configurations
If you already use a separate ESLint configuration and want to include eslint-config-next, ensure that it is extended last after other configurations. For example:
import { defineConfig, globalIgnores } from 'eslint/config'
import nextPlugin from '@next/eslint-plugin-next'
const eslintConfig = defineConfig([
nextPlugin.configs['core-web-vitals'],
// List of ignore patterns.
globalIgnores([]),
])
export default eslintConfigThe next configuration already handles setting default values for the parser, plugins and settings properties. There is no need to manually re-declare any of these properties unless you need a different configuration for your use case.
If you include any other shareable configurations, you will need to make sure that these properties are not overwritten or modified. Otherwise, we recommend removing any configurations that share behavior with the next configuration or extending directly from the Next.js ESLint plugin as mentioned above.
| Version | Changes |
|---|---|
v16.0.0 | next lint and the eslint next.config.js option were removed in favor of the ESLint CLI. A codemod is available to help you migrate. |
Was this helpful?