ESLint Configuration for TypeScript React Project

This document outlines the ESLint configuration used in our TypeScript React project. It provides a robust set of rules and plugins to ensure code quality and consistency.

Table of Contents

  1. Overview

  2. Prerequisites

  3. Installation

  4. Configuration File

  5. `Key Features`_

  6. `Usage`_

  7. `Customization`_

Overview

Our ESLint configuration is designed for a TypeScript React project using Vite. It incorporates rules for TypeScript, React, React Hooks, Vitest (for testing), and includes additional plugins for import sorting, accessibility, and code formatting.

Prerequisites

  • Node.js (version 14 or later recommended)

  • Yarn or npm

  • A TypeScript React project (preferably using Vite)

Installation

Install the required dependencies by running the following command in your project root:

yarn add -D eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-vitest eslint-plugin-testing-library eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-config-prettier eslint-import-resolver-typescript

Here’s a brief description of each package:

  • eslint: The core ESLint library

  • @typescript-eslint/eslint-plugin: ESLint plugin which provides rules for TypeScript codebases

  • @typescript-eslint/parser: An ESLint parser which leverages TypeScript ESTree to allow for ESLint to lint TypeScript source code

  • eslint-plugin-react: React specific linting rules for ESLint

  • eslint-plugin-react-hooks: ESLint plugin which provides rules for React Hooks

  • eslint-plugin-vitest: ESLint plugin for Vitest, a Vite-native testing framework

  • eslint-plugin-testing-library: ESLint plugin for Testing Library, providing rules to follow Testing Library best practices

  • eslint-plugin-import: ESLint plugin with rules that help validate proper imports

  • eslint-plugin-jsx-a11y: Static AST checker for accessibility rules on JSX elements

  • eslint-plugin-prettier: Runs Prettier as an ESLint rule and reports differences as individual ESLint issues

  • eslint-config-prettier: Turns off all rules that are unnecessary or might conflict with Prettier

  • eslint-import-resolver-typescript: This plugin adds TypeScript support to eslint-plugin-import

Configuration File

Create a file named eslint.config.js in your project root with the following content:

import { fixupPluginRules } from '@eslint/compat';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import vitestPlugin from 'eslint-plugin-vitest';
import testingLibrary from 'eslint-plugin-testing-library';
import importPlugin from 'eslint-plugin-import';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import prettier from 'eslint-plugin-prettier';
import prettierConfig from 'eslint-config-prettier';

export default [
  {
    files: ['src/**/*.{ts,tsx}'],
    ignores: ['**/dist/**', '**/node_modules/**', '**/coverage/**'],
    languageOptions: {
      parser: typescriptParser,
      parserOptions: {
        ecmaVersion: 'latest',
        sourceType: 'module',
        project: './tsconfig.json',
      },
    },
    plugins: {
      '@typescript-eslint': fixupPluginRules(typescript),
      react: fixupPluginRules(react),
      'react-hooks': fixupPluginRules(reactHooks),
      vitest: fixupPluginRules(vitestPlugin),
      'testing-library': fixupPluginRules(testingLibrary),
      import: fixupPluginRules(importPlugin),
      'jsx-a11y': fixupPluginRules(jsxA11y),
      prettier,
    },
    settings: {
      react: {
        version: 'detect',
      },
      'import/parsers': {
        '@typescript-eslint/parser': ['.ts', '.tsx'],
      },
      'import/resolver': {
        typescript: {
          alwaysTryTypes: true,
          project: './tsconfig.json',
        },
      },
    },
    rules: {
      ...typescript.configs.recommended.rules,
      ...react.configs.recommended.rules,
      ...reactHooks.configs.recommended.rules,
      ...vitestPlugin.configs.recommended.rules,
      ...testingLibrary.configs.react.rules,
      ...importPlugin.configs.recommended.rules,
      ...jsxA11y.configs.recommended.rules,
      ...prettierConfig.rules,
      'prettier/prettier': 'error',
      'react/react-in-jsx-scope': 'off',
      'react/jsx-uses-react': 'off',
      '@typescript-eslint/explicit-module-boundary-types': 'off',
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
      'react-hooks/exhaustive-deps': 'warn',
      'import/no-unresolved': 'error',
      'import/default': 'off',
      'import/no-named-as-default': 'off',
      'import/no-named-as-default-member': 'off',
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
          'newlines-between': 'always',
          alphabetize: { order: 'asc', caseInsensitive: true },
        },
      ],
      'jsx-a11y/anchor-is-valid': [
        'error',
        {
          components: ['Link'],
          specialLink: ['to'],
        },
      ],
    },
  },
  {
    files: ['src/**/*.test.{ts,tsx}'],
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      'vitest/expect-expect': 'error',
      'vitest/no-disabled-tests': 'warn',
      'vitest/no-focused-tests': 'error',
      'testing-library/await-async-queries': 'error',
      'testing-library/no-await-sync-queries': 'error',
      'testing-library/no-debugging-utils': 'warn',
      'testing-library/no-dom-import': 'off',
    },
  },
  {
    files: ['*.config.ts', 'vite.config.ts'],
    rules: {
      'import/no-extraneous-dependencies': 'off',
    },
  },
];