ESLint NEXT.js
Update NextConfig​
Update the next.config.ts
file with the following so files at the root level of the project are including when linting:
const nextConfig: NextConfig = {
eslint: {
dirs: ["."],
},
};
Modify ESLint Config​
There is an option for config files to be defined using typescript, eslint.config.mts
. It requires jiti
as an optional dev dependency. Native support for typescript without the need for jiti is in experimental as of Node.js >= 22.10.0 (See --experimental-strip-types flag).
ESLint v9 has transitioned to using Flat Configuration Files. eslint-plugin-next
is still transitioning to this new configuration format. ESLint's FlatCompat
utility is necessary to translate the eslintrc
format into flat config format.
(Last validated on 04/09/2025)
As of April 10th, 2025
- The
next
config extendseslint-plugin-react
,eslint-plugin-react-hooks
- The
next/core-web-vitals
extends thenext
config - The
next/typescript
is a wrapper config fortypescript-eslint
@eslint/js
is NOT included in any of thenext
lint configs
Install the following plugins and configs:
pnpm add --save-dev @eslint/compat @eslint/js @eslint/css @eslint/json @eslint/markdown
Modify the eslint.config.mjs
file to match the following:
import { fileURLToPath } from "node:url";
import { defineConfig } from "eslint/config";
import { includeIgnoreFile } from "@eslint/compat";
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
// import css from "@eslint/css"; // 🟡 TODO: Does this work yet for TailwindCSS v4.0?
import json from "@eslint/json";
import markdown from "@eslint/markdown";
const gitignorePath = fileURLToPath(new URL(".gitignore", import.meta.url));
const compat = new FlatCompat({
baseDirectory: import.meta.dirname,
});
export default defineConfig([
includeIgnoreFile(gitignorePath),
{
files: ["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],
extends: [
js.configs.recommended,
compat.extends("next/core-web-vitals", "next/typescript"),
],
},
// 🟡 TODO: If you are NOT using TailwindCSS then it should be safe to uncomment this object and
// remove the `languageOptions` property
// 🟡 TODO: Does this work yet for TailwindCSS v4.0?
// {
// files: ["**/*.css"],
// language: "css/css",
// languageOptions: {
// tolerant: true, // 🟡 This may be required due to custom syntax that TailwindCSS uses
// customSyntax: tailwindSyntax, // 🟡 This has not been updated yet for TailwindCSS v4.0
// },
// extends: [css.configs.recommended],
// },
{
files: ["**/*.json"],
ignores: ["package-lock.json"],
language: "json/json",
extends: [json.configs.recommended],
},
{
files: ["**/*.md"],
extends: [markdown.configs.recommended],
language: "markdown/gfm", // 🟡 Optional, include this property if using Github-flavored markdown
rules: {
"markdown/no-missing-label-refs": "off", // 🟡 Optional, this rule doesn't work with Github-flavored markdown alerts
},
},
]);
Custom Rules​
Add the following custom rules to your eslint.config.mjs
config.
/* ... */
export default defineConfig([
includeIgnoreFile(gitignorePath),
{
files: ["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],
extends: [js.configs.recommended, tseslint.configs.recommended],
rules: {
"no-console": ["error", { allow: ["warn", "error"] }],
"no-alert": "error",
"no-restricted-syntax": [
"error",
{
selector: "TSEnumDeclaration",
message:
"Enums are forbidden. Use string union types instead. Example: type Suit = 'HEARTS' | 'DIAMONDS' | 'SPADES' | 'CLUBS';",
},
],
},
},
/* ... */
]);
Docusaurus​
Docusaurus .gitignore
​
Merge the ./docusaurus/.gitignore
into the root .gitignore
so that eslint properly ignores the ignored docusaurus files. Example:
# ...
# Docusaurus
/docusaurus/node_modules
/docusaurus/build
.docusaurus
.cache-loader
Docusaurus plugin​
This plugin doesn't appear to be compatible with the Flat Configuration format of ESLint v9, and the rules it defines are not that important for most use cases
Review the Docusaurus ESLint Plugin Documentation for changes.
Print Full Config​
If you want to see the final rule set, all plugins, and names of all extended configs, use one of the following commands:
-
Using npx:
npx eslint --print-config src/file.tsx > tmp.config.json
-
Using pnpx:
pnpx eslint --print-config src/file.tsx > tmp.config.json
VS Code Extension​
Add the following to the .vscode/extensions.json
file.
{
"recommendations": [
. . .
"dbaeumer.vscode-eslint",
]
}
VS Code Settings​
Add the following to the .vscode/settings.json
file.
{
"eslint.useFlatConfig": true
}
Project Documentation​
Update 01-scripts.md
Documentation​
Add or update the following to the docusaurus/docs/01-scripts.md
documentation
| `pnpm lint` | Runs ESLint for all files that are NOT included in the `.gitignore` |
Create 02-code-quality/02-eslint.md
Documentation​
Create a docusaurus/docs/02-code-quality/02-eslint.md
file and add the following:
# ESLint
## What is ESLint
[ESLint](https://eslint.org/) is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs.
## No Enums Custom Rule
Our eslint config defines a custom rule that forbids the use of `enums`. The reason for this rule is best explained by this article: [Why you should use string literal unions over enums in TypeScript](../99-articles/why-you-should-use-string-literal-unions/index.md).
## Print Full Config
If you want to see the final rule set, all plugins, and names of all extended configs, use one of the following commands:
- Using npx: `npx eslint --print-config src/file.tsx > tmp.config.json`
- Using pnpx: `pnpx eslint --print-config src/file.tsx > tmp.config.json`
## Disable Node
To disable a line of code do the following:
```js
// Explanation of why there is a disable statement
// eslint-disable-next-line no-console
console.log("bar");
```
To disable a block of code do the following:
```js
// Explanation of why there is a disable statement
/* eslint-disable no-console */
console.log("bar");
/* eslint-enable no-console */
```
## Disable Linting Conventions
Please follow these 2 conventions When disabling a line or block of code:
**Convention 1** - Only disable the offending rule, not all of eslint. Example:
**Good** = `/* eslint disable no-console */`
**Bad** = `/* eslint disable */`
**Convention 2** - Provide a description explaining why the rule is disabled
The description must come after the configuration and needs to be separated from the configuration by two or more consecutive - characters. For example:
```js
// eslint-disable-next-line no-console -- Here's a description about why this configuration is necessary.
console.log("hello");
/* eslint-disable-next-line no-console --
* Here's a very long description about why this configuration is necessary
* along with some additional information
**/
console.log("hello");
```
## Ignore root files or directories
See ESLint documentation on [Ignoring Files and Directories](https://eslint.org/docs/latest/use/configure/ignore)
Update 98-vs-code/02-vs-code-extensions.md
Documentation​
Add the follow to the docusaurus/docs/98-vs-code/02-vs-code-extensions.md
documentation
| [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) | Provides immediate linting when writing code |
Create 99-articles/why-you-should-use-string-literal-unions
Article​
I'm paranoid about broken links due to relevant articles being removed. Therefore, when I find an article that I think is relevant to a project, I like to save a copy of that article within the project's documentation.
The 02-code-quality/02-eslint.md
documentation links to the following article
- Create a
docusaurus/docs/99-articles/why-you-should-use-string-literal-unions
directory. - Copy the Why You Should Use String Literal Unions Over Enums in TypeScript article contents into the above directory.