Forem

Cover image for Just use this Next.js Eslint Configuration
Jordan Haines
Jordan Haines

Posted on

3 1 1 2 2

Just use this Next.js Eslint Configuration

Here, Take this Configuration

I get it. If you're just here to find a good, working ESLint configuration for a NextJS project, then look no further. Copy what's below. Although, it's probably out of date, so you can find a version that's been updated since I published this post in my open source project Historio ➡️ in Github here ⬅️.

eslint.config.mjs

import globals from "globals"
import pluginJs from "@eslint/js"
import tseslint from "typescript-eslint"
import pluginReact from "eslint-plugin-react"
import eslintPluginUnicorn from "eslint-plugin-unicorn"
import tailwind from "eslint-plugin-tailwindcss"
import { FlatCompat } from "@eslint/eslintrc"
const compat = new FlatCompat({
// import.meta.dirname is available after Node.js v20.11.0
baseDirectory: import.meta.dirname,
})
/** @type {import('eslint').Linter.Config[]} */
const config = [
{ ignores: [".next/**", "public/**", "next.config.js", "postcss.config.js"] },
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
eslintPluginUnicorn.configs["flat/recommended"],
...tailwind.configs["flat/recommended"],
...compat.config({
extends: ["next"],
settings: {
next: {
rootDir: ".",
},
},
}),
...compat.config({
extends: ["plugin:drizzle/all"],
}),
{
rules: {
"no-undef": "error",
"react/react-in-jsx-scope": "off",
"tailwindcss/no-custom-classname": "off",
"@typescript-eslint/no-unused-vars": [
"error", // or "error"
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
"unicorn/prevent-abbreviations": "off",
},
},
{
files: ["**/*.{jsx,tsx}"],
rules: {
"no-console": "warn",
},
},
]
export default config
view raw gistfile1.txt hosted with ❤ by GitHub

Run this command to install necessary npm packages:

npm i --save eslint typescript-eslint eslint-config-next eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-tailwindcss eslint-plugin-unicorn
Enter fullscreen mode Exit fullscreen mode

Why Lint

When starting a new project, the very first thing I do is write some really sloppy, hacky, and messy code that (mostly) just works. I'm trying to validate an idea; to get a product off the ground.

This lasts all of about 2 pull requests before it gets out of hand, and I need some rules. You can see the exact moment in Historio where I realized inconsistent code was holding me back in this pull request where linting was first applied.

Having conventions on shared projects helps keep everyone aligned and working in the same direction. Conventions allow us to focus code reviews and discussion on the logic problems that matter instead of the semicolon placement or tab size that - frankly - only matter because they’re a distraction. Written conventions for a team, starting with a style guide but encompassing any rules that keep engineers focused on engineering, are thus essential.

The benefits to a team may be obvious, but even individual projects deserve conventions. Perhaps even stricter ones. Indeed, when I’m working on a project alone, I tend to codify a larger set of conventions than I would with a team simply because I get to do everything exactly my way. It's unclear if this is software engineering bliss or OCD. Every convention eliminates decisions I would otherwise have to make in the future (often many times over). Further, without a colleague to review every line of code I write, conventions help keep me consistent and tidy even when I really want to move fast.

How I Arrived at this Configuration

Other coders have thought longer and harder about conventions than I ever want to. When spinning up a new linting configuration or style guide, I look to what already exists. For my stack, this includes the following ESLint Plugins. For each, I start with their recommended config:

Additionally, here are a few plugins I evaluated but decided not to use:

  • AirBbB. I usually start here, but Vercel's config covered most of what I care about for both React and Node code styling.

  • XO. This feels like the Black of Typescript linting. XO has styles for everything and is very opinionated. This can be nice, because it takes a lot of code style decisions off of your plate. But beware it can be cumbersome to implement in the middle of a project because it will require extensive reformatting. In lieu of XO, I found Unicorn opinionated enough and more immediately useful.

Technical Note on ESLint Config

There are too many ways to configure ESLint. I started with the .eslintrcs.json config that came from create-next-app. However, this configuration is now deprecated in favor of eslint.config.mjs.

Because I couldn't easily extend this deprecated configuration, I (swore and then) scrapped it and initialized a new eslint config with npm init @eslint/config@latest. I then added the configuration for next wrapped with eslint's flat compat utility. Here's the snippet in eslint.config.mjs:

import { FlatCompat } from "@eslint/eslintrc"
export default const config = [
// other ESLint plugins and rulesets
...compat.config({
    extends: ["next"],
    settings: {
      next: {
        rootDir: ".",
      },
    },
  }),
]
Enter fullscreen mode Exit fullscreen mode

Most other plugins offer a configuration that can be plugged into eslint.config.mjs directly. But if you see an error starting with:

Support for loading ES Module in require() is an experimental feature and might change at any time
then you probably need to wrap a plugin with that flat config migration utility

Sharpen Skills by Exploring Linting Config

Ultimately, there is no single "right" style guide or linting configuration. You should choose what works best for your project. When creating or revisiting my configurations, I often uncover new tricks or coding conventions that make me a better software engineer. when putting together this configuration, I learned:

  • It's important to deal with the ambiguity of null and undefined in JS/TS, but how you do it is project-dependent. By default, unicorn encourages use of undefined over null always, but there are very legitimate reasons to use null, like if you have a lot of code that leverages an ORM with null values. This long thread has many great points and no single right answer.

  • Avoid passing a function reference directly to iterators (i.e. {elements.map(callback)}) details

Your style guide also shouldn't be static. It should evolve as your project grows. Not only do the needs of your project change, but conventions, libraries, and coding languages evolve around your work, too. It's okay (good, even) to make considered and reasoned changes to your conventions, even if you're backtracking on something you once felt was really important.

Reinvent your career. Join DEV.

It takes one minute and is worth it for your career.

Get started

Top comments (1)

Collapse
 
jordanahaines profile image
Jordan Haines

What do you include in your Next.JS linting that I missed?

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay