A Complete guide to setup ESLint and Prettier for a React project with Typescript

A Complete guide to setup ESLint and Prettier for a React project with Typescript

Why do we need ESLint and Prettier?

To keep it short, Prettier is used for code formatting and ESLint is used for catching bugs!

ESLint

ESLint is a tool that statically identifies and reports patterns found in JavaScript code, to make code more consistent and avoid bugs. ESlint can be configured based on the developer’s requirements.

A few examples are:

To check the various linting rules in ESLInt. Please check https://eslint.org/docs/latest/rules/

Prettier

Prettier on the other hand is a code formatting tool, which helps developers to make their code clean and readable.

A few examples are:

Create a React Project

To create a React project with TypeScript. Open a new terminal and run

 
  npx create-react-app my-app --template typescript

Setup ESLint and Prettier

Install ESLint

 
  npm install --save-dev eslint

Install Prettier

 
  npm install --save-dev prettier

Install ESLint Plugins

These dependencies are required to make sure ESLint works well with various plugins like prettier, react and typescript-eslint.

 
  npm install --save-dev eslint-plugin-prettier
npm install --save-dev eslint-config-prettier
npm install --save-dev @typescript-eslint/eslint-plugin
npm install --save-dev eslint-plugin-react
npm install --save-dev eslint-config-airbnb-typescript
npm install --save-dev eslint-plugin-import
npm install --save-dev @typescript-eslint/parser

ESLint Configuration

Add the ESLint configuration .eslintrc.{js,yml,json} file to the root level of the project folder. Create .eslintrc.js and copy and paste the below code snippet

 
  module.exports = {
  root: true, // Make sure eslint picks up the config at the root of the directory
  settings: {
    react: {
      version: 'detect', // Automatically detect the react version
    },
  },
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  extends: [
    'plugin:react/recommended',
    'airbnb-typescript',
    'plugin:prettier/recommended', // Make this the last element so prettier config overrides other formatting rules
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: ['./tsconfig.json'],
  },
  plugins: ['@typescript-eslint', 'react', 'import'],
  rules: {
    'prettier/prettier': ['error', {}, { usePrettierrc: true }], // Use our .prettierrc file as source

    //eslint/react/rules
    'react/jsx-props-no-spreading': 'off',
    'react/require-default-props': 'off',
    'react/jsx-pascal-case': 'error',

    '@typescript-eslint/naming-convention': [
      'error',
      {
        selector: 'typeLike',
        format: ['PascalCase'],
        filter: { regex: '^(__String|[A-Za-z]+_[A-Za-z]+)$', match: false },
      },
      {
        selector: 'interface',
        format: ['PascalCase'],
        custom: { regex: '^I[A-Z]', match: false },
        filter: {
          regex: '^I(Arguments|TextWriter|O([A-Z][a-z]+[A-Za-z]*)?)$',
          match: false,
        },
      },
      {
        selector: 'variable',
        format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
        leadingUnderscore: 'allow',
        filter: {
          regex: '^(_{1,2}filename|_{1,2}dirname|_+|[A-Za-z]+_[A-Za-z]+)$',
          match: false,
        },
      },
      {
        selector: 'function',
        format: ['camelCase', 'PascalCase'],
        leadingUnderscore: 'allow',
        filter: { regex: '^[A-Za-z]+_[A-Za-z]+$', match: false },
      },
      {
        selector: 'parameter',
        format: ['camelCase'],
        leadingUnderscore: 'allow',
        filter: { regex: '^(_+|[A-Za-z]+_[A-Z][a-z]+)$', match: false },
      },
      {
        selector: 'method',
        format: ['camelCase', 'PascalCase'],
        leadingUnderscore: 'allow',
        filter: { regex: '^[A-Za-z]+_[A-Za-z]+$', match: false },
      },
      {
        selector: 'memberLike',
        format: ['camelCase'],
        leadingUnderscore: 'allow',
        filter: { regex: '^[A-Za-z]+_[A-Za-z]+$', match: false },
      },
      {
        selector: 'enumMember',
        format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
        leadingUnderscore: 'allow',
        filter: { regex: '^[A-Za-z]+_[A-Za-z]+$', match: false },
      },
      { selector: 'property', format: null },
    ],

    '@typescript-eslint/no-unused-vars': [
      'off',
      { varsIgnorePattern: '^_', argsIgnorePattern: '^_' },
    ],
    '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],

    'no-duplicate-imports': 'off',
    '@typescript-eslint/no-duplicate-imports': 'error',

    '@typescript-eslint/no-inferrable-types': 'error',
    '@typescript-eslint/no-misused-new': 'error',
    '@typescript-eslint/no-this-alias': 'error',

    'no-unused-expressions': 'off',
    '@typescript-eslint/no-unused-expressions': ['error', { allowTernary: true }],

    '@typescript-eslint/prefer-for-of': 'error',
    '@typescript-eslint/prefer-function-type': 'error',
    '@typescript-eslint/prefer-namespace-keyword': 'error',
    '@typescript-eslint/explicit-function-return-type': 'error',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/no-var-requires': 'error',
    '@typescript-eslint/prefer-nullish-coalescing': 'error',
    '@typescript-eslint/unbound-method': 'off',
    '@typescript-eslint/prefer-as-const': 'error',

    // eslint
    'constructor-super': 'error',
    curly: ['error', 'multi-line'],
    'dot-notation': 'error',
    eqeqeq: 'error',
    'new-parens': 'error',
    'no-caller': 'error',
    'no-duplicate-case': 'error',
    'no-empty': 'error',
    'no-eval': 'error',
    'no-extra-bind': 'error',
    'no-fallthrough': 'error',
    'no-new-func': 'error',
    'no-new-wrappers': 'error',
    'no-return-await': 'error',
    'no-sparse-arrays': 'error',
    'no-template-curly-in-string': 'error',
    'no-throw-literal': 'error',
    'no-trailing-spaces': 'error',
    'no-undef-init': 'error',
    'no-unsafe-finally': 'error',
    'no-unused-labels': 'error',
    'no-var': 'error',
    'object-shorthand': 'error',
    'prefer-const': 'error',
    'prefer-object-spread': 'error',
    'space-in-parens': 'error',
    'unicode-bom': ['error', 'never'],
    'use-isnan': 'error',
    'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
  },
};

Prettier Configuration

Create .prettierrc at the root level of the project folder and copy and paste the below code snippet.

 
  {
  "semi": true,
  "tabWidth": 4,
  "printWidth": 100,
  "singleQuote": true,
  "trailingComma": "none",
  "bracketSameLine": true,
  "endOfLine": "auto"
}

Ignore Files from ESLint Checking

Sometimes we don’t want the linting check to be performed on certain files for example they can be generated code like x, build code or other configuration files.

In that case, Create a .eslintignore file at the root level of the project folder and copy and paste the below code snippet.

 
  node_modules
build
.github
.eslintrc.js
src/service-worker.ts
src/serviceWorkerRegistration.ts
src/setupTests.ts
src/reportWebVitals.ts
src/react-app-env.d.ts

Ignore Files from Prettier Code Formatter

Similar to ignoring the linting for generated code. We also want to ignore code formatting on those files as well.

In that case, Create a .prettierignore file at the root level of the project folder and copy and paste the below code snippet.

 
  node_modules
build

Integration with package.json

Once all the dependencies and configuration files have been added to the project. We can add scripts in package.json to run the linter and code formatting using the terminal.

Copy and paste the below code snippet into the script section of package.json.

 
  "lint": "eslint --fix .",
"lint-check": "eslint .",
"format": "prettier --write . --config ./.prettierrc",
"format-check": "prettier --check . --config ./.prettierrc"

Here lint and format will resolve fixable issues. On the other hand, link-check and format-check will only report the issues without fixing them. Below code snippet shows how to run the scripts.

 
  npm run lint
npm run lint-check
npm run format
npm run format-check

Integration with VS Code

We can integrate ESLint and Prettier with VS Code by installing the VS Code Extensions ESLint and Prettier - Code formatter.

Set Prettier as Default Formatter

  1. To open the command palette, you can use COMMAND + SHIFT + P on macOS or CTRL + SHIFT + P on Windows.
  2. In the command palette, search for format and then choose Format Document.
  3. Click the Configure Default Formatter… button
  4. Then choose Prettier - Code Formatter.

Integration with GitHub Actions

We are going to create a Check action which checks for linting and code formatting when the pull request is raised.

Create .github/workflows folders in the root level of the project. Then create a Check.yml to add the metadata to perform the GitHub Action.

 
  name: Check
on: [pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
      - name: Set NPM Registry for installing dependencies
        run: npm config set registry https://registry.npmjs.com/
      - name: Install dependencies
        run: npm ci
      - name: Generate build code
        run: npm run build --if-present
      - name: Run formatter
        run: npm run format
      - name: Run linter
        run: npm run lint-check