ESLint as a formatter in Next.js in TypeScript:

Let me start with a confession: after having completed a few projects with Next.js (including this website.) I have always struggled to make it play well with ESLint and Prettier in TypeScript.

Next.js already comes with an implementation of ESLint that will allow you to check your code with a single command. The difference here is that we want to check and format the code as soon as we type it.

Packages to install

The packages below are the ones I am currently using. They cover what is needed for TypeScript and also to support React (needed for Next.js).

Additionally, I've decided to use airbnb's rules as a base before adding some. That explains the eslint-config-airbnb package

yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-next eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-prettier prettier

Configs to add

ESLint:

After running the initial ESLint configuration, a eslintrc.json file will be created on your root directory. A few extra tweaks are needed to get it working optimally.

I have manually specified some extra plugins such as the prettier related ones below and also the prettier one.

Important: the prettier plugin must always be the last one in the last. That is due to cascading specificity. Leaving it as the last one guarantees that it will not be overridden by one of the others.

"extends": [
	"next/core-web-vitals",
	"airbnb",
	"plugin:prettier/recommended",
	"plugin:prettier/react",
	"plugin:prettier/@typescript-eslint",
	"prettier"
	],
"parser": "@typescript-eslint/parser", // this line is important if you are using TS

The same applies for the plugin section of the config:

"plugins": ["react", "@typescript-eslint", "prettier"]

So this becomes you final eslintrc.json file

{
    "env": {
        "browser": true,
        "es2021": true,
        "node": true,
        "jest": true
    },
    "extends": [
        "next",
        "next/core-web-vitals",
        "prettier",
        "plugin:@typescript-eslint/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": "latest",
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "plugins": [
        "prettier",
        "@typescript-eslint"
    ],
    "rules": {
        "prettier/prettier": [
            "error",
            {
                "printWidth": 120,
                "bracketSpacing": true,
                "singleQuote": true,
                "semi": false,
                "trailingComma": "es5"
            }
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ],
        "eqeqeq": "error",
        "no-var": "error",
        "prefer-arrow-callback": "warn",
        "prefer-const": "error",
        "no-console": "error",
        "no-duplicate-case": "warn",
        "@typescript-eslint/no-unused-vars": "warn",
        "@typescript-eslint/no-explicit-any": "off",
        "@typescript-eslint/ban-ts-comment": "off"
    }
}

VSCode setup:

At this point your project already has a folder called .vscode (hidden folder). If the folder is not there you can create it and in it, add a file called settings.json. In the file you will want to add the default formatting configuration for your project. Here's how my configuration looks like

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnPaste": true,
  "editor.formatOnSave": true,
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnPaste": true,
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnPaste": true,
    "editor.formatOnSave": true
  }
}

You will notice that at first I am setting the overall settings for vscode.

  1. Default formatter as prettier
  2. format on paste on
  3. format on save on.

That will get your basics covered, however, for me it did not work for typescript and jsx. Due to that I add the next rules specific for typescript and typescript react.

Rules to consider

In the ESLint website you can see all the possible rules you can make use of and add to your ESLint config. As soon as a rule is added, ESLint will start checking for it.

Below you can see the compilation of the rules I currently have active on most of my projects.

Feel free to take it, mix it up and adapt to your own taste.

"rules": {
	"prettier/prettier": "error",
	"react/jsx-filename-extension": [
		1,
		{ "extensions": [".js", ".jsx", ".ts", ".tsx"] }
		],
	"@typescript-eslint/no-unused-vars": "error",
	"@typescript-eslint/no-explicit-any": "error",
	"import/prefer-default-export": 0,
	"@typescript-eslint/array-type": [
		"error",
		{
		"default": "array-simple"
		}
		],
	"@typescript-eslint/camelcase": "off",
	"@typescript-eslint/explicit-function-return-type": "off",
	"@typescript-eslint/no-empty-interface": "off",
	"@typescript-eslint/no-non-null-assertion": "off",
	"@typescript-eslint/no-use-before-define": "off",
	"@typescript-eslint/explicit-module-boundary-types": "off",
	"object-shorthand": "error",
	"jsx-a11y/anchor-is-valid": "off",
	// @typescript-eslint/recommended changes
	"@typescript-eslint/no-unsafe-member-access": 1,
	"@typescript-eslint/ban-ts-comment": "off",
	"@typescript-eslint/no-unsafe-return": 1,
	"@typescript-eslint/no-empty-function": 0,
	"@typescript-eslint/no-unsafe-assignment": 1,
	"@typescript-eslint/no-var-requires": "off",
	"@typescript-eslint/ban-types": "off",
	"@typescript-eslint/no-unsafe-call": 1,
	"semi": 0,
	"no-extra-semi": 0,
	"eslint-comments/no-unlimited-disable": 0,
	"no-bitwise": "off",
	"no-shadow": [
		"off",
		{
		"hoist": "all"
		}
		],
	"jsx-quotes": 0,
	"react-native/no-inline-styles": 0,
	"curly": ["error", "multi-line"],
	"no-undef": 0,
	"no-catch-shadow": 0,
	"no-useless-escape": 0,
	"no-console": "error",
	"no-spaced-func": 0
}

Conclusion

With this configuration in place you should now be able to get automatic formatting and checking on your Next.js projects.

Enjoy.


Hit me up on twitter: @jorgelopes_r