loading...
Cover image for ESlint and Prettier for React apps (Bonus - Next.js and TypeScript)

ESlint and Prettier for React apps (Bonus - Next.js and TypeScript)

arpit73 profile image Arpit ・6 min read

What if you could catch and fix your bugs before you even run your program. What if you could write code that has the same syntax and follows the same guidelines throughout the entire project. What if all of this could be enforced for your entire team of developers without any headaches. Seems amazing right? This is achieved by the amazing tools called ESlint and Prettier. Read about how you can add these tools to your JavaScript or TypeScript projects to improve your code quality and consistency.

This article will focus on ReactJS and also delve into NextJS with TypeScript, but these tools work for a JavaScript or TypeScript codebase in general and can be configured to work on any JavaScript project, with Prettier you can even target auto formatting for other languages like HTML, CSS, SCSS/SASS, Markdown, JSON, YAML, GraphQL, styled-components and many more!!!


NOTE

Eslint and Prettier are two separate tools, they can be run independently of each other, but we will leverage some very helpful ESlint plugins to combine them for maximum effect with minimal configuration.


ESlint

First I'll talk about ESlint. It is a static code analyzer, that means it tells you errors and mistakes that you may make while you are developing.

These errors can be stuff like -

  • Simple syntax errors eg. not closing a function declaration with }.
  • Dead code detection eg. unused variables, code written after a return statement.
  • Violating code guidelines, these are rules defined by yourself or a combination of predefined standards like the Airbnb styled guide or Google's style guide etc.

Prettier

Prettier is a code formatter, it's only concerned with how your code looks, do you want ensure consistent indentation in the entire project?
Do you want to ensure there're no semicolons in the project? Make your promise chains look perfectly consistent and readable? Prettier can be enabled for the entire project and instead of your team disagreeing about formatting styles, you can just leave it all to Prettier to figure out.

Getting started

Make sure you have npm and Node.js installed.

Let's install the devDependencies for ESlint -

npm i -D eslint \                       # Eslint itself
         prettier \                     # Prettier itself
         eslint-plugin-react \          # Eslint plugin for react
         eslint-plugin-react-hooks \    # Eslint plugin for react hooks, helps you write modern functional react components
         eslint-config-prettier \       # Eslint config for prettier, it will extend the style guide to match prettier
         eslint-plugin-prettier \       # Eslint plugin for prettier, it will raise eslint errors about formatting
         eslint-plugin-jsx-a11y         # Eslint plugin to raise accessibility violation errors

NOTE

If you are using create-react-app then you already have Eslint baked in, you only need install the other plugins etc.


Making the config files -

touch .eslintrc.js .prettierrc

.eslintignore and .prettierignore

These are the files we need for telling eslint and prettier not to target certain files and folder

touch .eslintignore .prettierignore

Just add the following to both files

node_modules

Alternatively if you just wish to use your .gitignore file as ignore path you can pass it with a flag when running ESlint
eg.

eslint --fix . --ignore-path ./.gitignore

Let's populate our configs now -

.prettierrc

This is the config that I use for prettier -

{
    "semi": true,
    "tabWidth": 4,
    "printWidth": 100,
    "singleQuote": true,
    "trailingComma": "none",
    "jsxBracketSameLine": true
}

.eslintrc.js

This is a base config for eslint, we can extend it further with cool plugins.

module.exports = {
    root: true,                 // Make sure eslint picks up the config at the root of the directory
    parserOptions: {
        ecmaVersion: 2020,      // Use the latest ecmascript standard
        sourceType: 'module',   // Allows using import/export statements
        ecmaFeatures: {
            jsx: true           // Enable JSX since we're using React
        }
    },
    settings: {
        react: {
            version: 'detect'   // Automatically detect the react version
        }
    },
    env: {
        browser: true,          // Enables browser globals like window and document
        amd: true,              // Enables require() and define() as global variables as per the amd spec.
        node: true              // Enables Node.js global variables and Node.js scoping.
    },
    extends: [
        'eslint:recommended',
        'plugin:react/recommended',
        'plugin:jsx-a11y/recommended',
        'plugin:prettier/recommended' // Make this the last element so prettier config overrides other formatting rules
    ],
    rules: {
        'prettier/prettier': ['error', {}, { usePrettierrc: true }]  // Use our .prettierrc file as source
    }
};

Running ESlint

You need to add the script to your package.json file.

  "scripts": {
    "lint": "eslint --fix .",
    "format": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc"
  },

Now simply run

npm run lint

Targeting Next.js

next.js logo
For using Eslint with Next.js not a lot of changes are needed, we'll add two rules -

For handling the need for having react in context while writing JSX code since Next.js doesn't require that.
For handling accessibility related errors raised by the jsx-a11y plugin.
In Next.js we use the Link component for wrapping the <a></a> tag. Instead of passing href attribute to <a></a> we pass it as prop to Link instead. This raises a very common accessibility issue of an anchor tag with no href, and we don't want any eslint errors so we need to figure out a way to handle this situation.

Simply go to your .eslint.js and add our custom rules for it.

{
    rules: {
        'react/react-in-jsx-scope': 'off',
        'jsx-a11y/anchor-is-valid': [
            'error',
            {
                components: ['Link'],
                specialLink: ['hrefLeft', 'hrefRight'],
                aspects: ['invalidHref', 'preferButton']
            }
        ]
    }
}

Targeting TypeScript

typescript logo

For TypeScript, we need to make some additions to our devDependencies.

npm i -D @typescript-eslint/eslint-plugin \     # For extending our rules to work with prettier
         @typescript-eslint/parser              # To enable eslint to parse typescript code

Now we need to modify the .eslintrc.js file -

Add a top level property to our config to handle typescript code parsing

module.exports = {
    parser: '@typescript-eslint/parser'
};

Add the following new items to the extends array.

{
    extends: [
        'plugin:@typescript-eslint/eslint-recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier/@typescript-eslint',
    ]
}

Finally

This is what your .eslintrc.js should look like after Typescript and Next.js additions.

module.exports = {
    root: true,
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true
        }
    },
    settings: {
        react: {
            version: 'detect'
        }
    },
    env: {
        browser: true,
        amd: true,
        node: true
    },
    extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/eslint-recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:jsx-a11y/recommended',
        'prettier/@typescript-eslint',
        'plugin:prettier/recommended'   // Make sure this is always the last element in the array.
    ],
    rules: {
        'prettier/prettier': ['error', {}, { usePrettierrc: true }],
        'react/react-in-jsx-scope': 'off',
        'react/prop-types': 'off',
        '@typescript-eslint/explicit-function-return-type': 'off',
        'simple-import-sort/sort': 'error',
        'jsx-a11y/anchor-is-valid': [
            'error',
            {
                components: ['Link'],
                specialLink: ['hrefLeft', 'hrefRight'],
                aspects: ['invalidHref', 'preferButton']
            }
        ]
    }
};

Bonus Content

eslint-plugin-simple-import-sort

Do you find yourself worrying about the order of your import statements. Here's a solution to let eslint worry about all that.

npm i -D eslint-plugin-simple-import-sort

Before - Before sorting
After -
After sorting

Update your .eslintrc.js file to make use of this plugin. Add a top level property plugins.

module.exports = {
    plugins: ['simple-import-sort']
};

husky + lint-staged

To make sure you're code is formatted and has no linting errors you'll have to run npm run lint every time. We should be able to automate this somehow.

Let's go -

npm i -D husky \        # A tool for adding a pre-commit hook to git, It will run a certain command every time you commit
         lint-staged    # A tool for running a certain list of commands over files that staged for committing in git

Now in your package.json add the following at the top level.

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "./**/*.{js,jsx,ts,tsx}": [
      "eslint --fix",
    ]
  }
}

Conclusion

Hope you learned something new. Eslint is endlessly customizable and you should explore more to find some plugins and configs that best benefit your project.

All the code snippets can be found here as a Gist on GitHub

Posted on by:

arpit73 profile

Arpit

@arpit73

Full Stack Developer | React.js | Node.js | MongoDB

Discussion

markdown guide