Nikhil
Nikhil

Reputation: 604

next.js Setting up ESLint for NextJs

I have created basic next.js app using "npx create-next-app" and .eslintrc.json file created to add eslint rules.but it's not working.how to add linting rules to nextjs config

{
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": [
        "standard"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly",
        "React": "writable"
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "react/react-in-jsx-scope": "off"
    }
}

I have tried this solution - https://medium.com/@joelmasters/setting-up-eslint-for-nextjs-37163d4cabaa

Upvotes: 24

Views: 66286

Answers (3)

GorvGoyl
GorvGoyl

Reputation: 49150

Update

NextJS now has official guide to add eslint to project: https://nextjs.org/docs/basic-features/eslint Additionally you need to install ESLint extension.

Also, If you're looking for ESLint with typescript support: https://gourav.io/blog/nextjs-cheatsheet

Old answer:

Install ESLint

npm i eslint --save-dev

Install ESLint plugins:

npx install-peerdeps --dev eslint-config-airbnb

Above single command will install 6 plugins: eslint-config-airbnb, eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-jsx-a11y. You can also install these plugins individually.

Install babel eslint

npm i -D babel-eslint

Install prettier plugin (optional, so that prettier doesn't mess up with linting)

 npm i -D eslint-config-prettier eslint-plugin-prettier

Your "devDependencies" should look something like this:

"devDependencies": {
    "babel-eslint": "^10.1.0",
    "eslint": "^6.8.0",
    "eslint-config-airbnb": "^18.1.0",
    "eslint-config-prettier": "^6.11.0",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-jsx-a11y": "^6.2.3",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-react": "^7.20.0",
    "eslint-plugin-react-hooks": "^2.5.1"
  }

Now, create a file .eslintrc.json at root of project. Paste below config:

{
  "env": {
    "browser": true,
    "commonjs": true,
    "es6": true,
    "node": true
  },
  "parser": "babel-eslint",
  "extends": [
    "eslint:recommended",
    "airbnb",
    "airbnb/hooks",
    "plugin:react/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
    "plugin:jsx-a11y/recommended",
    // "plugin:react-hooks/recommended",
    // always put prettier at last
    "prettier"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true // enable linting for jsx files
    },
    "ecmaVersion": 11,
    "sourceType": "module"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "plugins": ["react", "react-hooks"],
  "rules": {
    // NextJs specific fix: suppress errors for missing 'import React' in files for nextjs
    "react/react-in-jsx-scope": "off",
   // NextJs specific fix: allow jsx syntax in js files
    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], //should add ".ts" if typescript project
    "react/display-name": 1
  }
}

Also, install ESLint extension for VSCode.

Reload VSCode window once to get proper linting

ESLint will automatically start detecting errors/warnings in *.js and *.jsx files. If that's not the case then either your project has no linting errors or ESLint is not properly setup. To test if linting works run eslint command in terminal with folder path i.e. eslint pages/** and notice output.

To disable linting of some files/folders you can create a .eslintignore at the root of project.

.eslintignore:

# don't ever lint node_modules
node_modules
# don't lint build output (make sure it's set to your correct build folder name)
dist
# don't lint nyc coverage output
coverage

Finally, you can also add linting to scripts in package.json as a part of your build/deploy process:

"scripts": {
    "lint": "eslint ./components/** ./pages/** -c .eslintrc.json --ext js,jsx",
    "lint-fix": "eslint ./components/** ./pages/** -c .eslintrc.json --fix --ext js,jsx",
}

See my current ESLint configuration for NextJS Typescript project: https://github.com/GorvGoyl/Personal-Site-Gourav.io/blob/main/.eslintrc.js

Upvotes: 37

Official in-tree examples

As mentioned by GorvGoyl, some better integration was added around Next.js 11.

Although there is documentation at: https://nextjs.org/docs/basic-features/eslint being a eslint newbie I just couldn't understand what I was supposed to do, so I just looked under examples/ and found some actual working code:

examples/with-eslint

https://github.com/vercel/next.js/tree/v12.0.7/examples/with-eslint

Minimal eslint example.

The setup contains:

package.json

{
  "name": "with-eslint",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "license": "MIT",
  "dependencies": {
    "next": "12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "eslint": "^7.24.0",
    "eslint-config-next": "12.0.7"
  }
}

.eslintrc

{
  "extends": "next",
  "root": true
}

pages/index.js

const Home = () => (
  <div>
    <script src="https://fake-script.com" />
    <p>Home</p>
  </div>
)

export default Home

For example doing:

cd examples/with-eslint
npm install
npm run lint

gives the desired error:

3:5 Warning: External synchronous scripts are forbidden. See: https://nextjs.org/docs/messages/no-sync-scripts. @next/next/no-sync-scripts

We can modify pages/index.js a bit to add some more cases that we might want to fail:

import React from 'react'

const Home = () => {
  let s = 'abc'
  s = "abc"
  let unused
  if (false) {
    React.useEffect(() => 1)
  }
  return (
    <div>
      <script src="https://fake-script.com" />
      <p>Home</p>
    </div>
  )
}

export default Home

and the results are:

examples/with-typescript-eslint-jest

https://github.com/vercel/next.js/tree/v12.0.7/examples/with-typescript-eslint-jest

Exmple also with typescript.

Note that this example cannot be run in-tree otherwise it fails with:

ESLint couldn't find the plugin "eslint-plugin-jest".

which is must be picking up from repo toplevel:

The plugin "eslint-plugin-jest" was referenced from the config file in "../../.eslintrc.json#overrides[0]".

you have to first copy it outside somewhere like:

cp -rv examples/with-typescript-eslint-jest /tmp
cd /tmp/with-typescript-eslint-jest

But I think this examples is a bit outdated as it is not using the"

  "extends": "next",

preset.

My recommended Next 12 typescript + prettier setup

Since the in-tree "examples/with-typescript-eslint-jest" doesn't look very up-to-date, here's a version of it that should be (just without jest):

package.json

{
  "name": "with-eslint",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "format": "prettier --ignore-path .gitignore --write .",
    "type-check": "tsc"
  },
  "license": "MIT",
  "dependencies": {
    "install": "0.13.0",
    "next": "12.0.7",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "@types/node": "12.12.21",
    "@types/react": "17.0.2",
    "@types/react-dom": "17.0.1",
    "eslint": "8.5.0",
    "eslint-config-next": "12.0.7",
    "eslint-config-prettier": "7.2.0",
    "eslint-plugin-prettier": "4.0.0",
    "prettier": "2.5.1",
    "typescript": "4.5.4"
  },
  "prettier": {
    "printWidth": 80,
    "semi": false,
    "singleQuote": true
  }
}

.eslintrc.json

{
  "extends": ["eslint:recommended", "next", "prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error"
  }
}

tsconfig.json (autogenerated by Next when you run npm run dev)

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

.gitignore

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel

# typescript
*.tsbuildinfo

pages/index.tsx

import React from 'react'

const Home = () => {
  let n: number
  let s = 'abc'
  s = "abc"
  let unused
  if (false) {
    React.useEffect(() => 1)
  }
  return (
    <div>
      <script src="https://fake-script.com" />
      <p>Home</p>
    </div>
  )
}

export default Home

With this setup doing:

npm run lint

catches all issues as we would like:

4:7  Error: 'n' is defined but never used.  no-unused-vars
6:3  Error: 's' is assigned a value but never used.  no-unused-vars
6:7  Error: Replace `"abc"` with `'abc'`  prettier/prettier
7:7  Error: 'unused' is defined but never used.  no-unused-vars
8:7  Error: Unexpected constant condition.  no-constant-condition
9:5  Error: React Hook "React.useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.  react-hooks/rules-of-hooks
13:7  Warning: External synchronous scripts are forbidden. See: https://nextjs.org/docs/messages/no-sync-scripts.  @next/next/no-sync-scripts

The prettier errors can be fixed automatically with autoformatting:

npm run format

which automatically patches for us:

--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -3,7 +3,7 @@ import React from 'react'
 const Home = () => {
   let n: number
   let s = 'abc'
-  s = "abc"
+  s = 'abc'
   let unused
   if (false) {

Running:

npm run type-check

catches the type error:

pages/index.tsx:9:27 - error TS2322: Type 'number' is not assignable to type 'void | Destructor'.

9     React.useEffect(() => 1)

Both npm run lint and npm run type-check are automatically run by npm run build.

Lint is enabled only on pages/, components/ and lib/ by default

As mentioned at: https://nextjs.org/docs/basic-features/eslint#linting-custom-directories-and-files

To enable it everywhere in the projects:

module.exports = {
  eslint: {
    dirs: ['.'],
  },
}

Upvotes: 1

Hadnazzar
Hadnazzar

Reputation: 1616

you need to install required npm modules.

with Npm:

npm i -D babel-eslint eslint-config-airbnb eslint eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks

with Yarn:

yarn add -D babel-eslint eslint-config-airbnb eslint eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks

Here is related article about that

https://medium.com/@melih193/next-js-eslint-setup-tutorial-for-airbnb-config-c2b04183a92a

Upvotes: 15

Related Questions