Modern ESLint configuration with TypeScript, React/Next.js, YAML, Testing Library, and testing framework support using ESLint v9+ flat config format.
- Features
- Installation
- Usage
- Prettier Configuration
- Example scripts
- Commitlint Configuration
- Migration from Legacy Config
- Modern ESLint v9+ flat config: Uses the new flat configuration format
- Dynamic feature detection: Automatically configures based on your project's dependencies
- TypeScript support: Full TypeScript linting with modern typescript-eslint parser and rules
- React ecosystem: React, React Hooks, and JSX accessibility rules when React is detected
- Next.js support: Automatically configures Next.js official plugin linting rules when detected
- Test framework agnostic: Supports Jest and Vitest with automatic detection
- Testing Library integration: Automatically includes Testing Library rules for test files
- YAML/YML support: Built-in linting for YAML configuration files
- Prettier integration: Built-in Prettier configuration with conflict resolution via eslint-config-prettier
- ESM architecture: Built with ECMAScript modules and full TypeScript typing
- Docker support: Optional configuration for dockerized applications
- Blockchain/dApp support: Optional configuration for decentralized applications
Install the package and required peer dependencies:
npm i -D eslint@9 @codfish/eslint-config
# Optionally, you can uninstall plugins or presets you don't need to manage anymore,
# @codfish/eslint-config includes them all.
npm uninstall typescript-eslint \
eslint-config-prettier \
eslint-plugin-jest \
eslint-plugin-jsx-a11y \
eslint-plugin-prettier \
eslint-plugin-react \
eslint-plugin-react-hooks \
eslint-plugin-simple-import-sort \
eslint-plugin-testing-library \
eslint-plugin-yml \
@next/eslint-plugin-next \
eslint-plugin-next \
commitlint \
@commitlint/cli \
@commitlint/config-conventional
Note
Optionally, you can uninstall prettier as well. If you don't have prettier installed already but you want to format
other file types (like Markdown, JSON, CSS, etc.), you can install it as a dev dependency: npm i -D prettier
. Then
you can use Prettier to format your non-JS files directly. Eslint will still run Prettier as an ESLint rule.
Create an eslint.config.js
file in your project root:
import { defineConfig } from 'eslint/config';
import codfish from '@codfish/eslint-config';
export default defineConfig(
codfish,
{
rules: {
// Relax some rules for your project
'react/prop-types': 'off',
'import/prefer-default-export': 'off',
'@typescript-eslint/explicit-function-return-type': 'warn',
},
},
);
[!IMPORTANT] If you get ES module errors, you may need to set the
type
field in yourpackage.json
tomodule
or rename your config file toeslint.config.mjs
.
Using extends and targetting all files:
import codfish from '@codfish/eslint-config';
export default defineConfig({
extends: [codfish],
rules: {
// temporary
'@typescript-eslint/no-explicit-any': 'off',
},
});
Using extends and targetting specific files:
Warning
Not recommended. This will prevent it from using the files
specified in the main config, so rules around test
files, yml files, etc. will not be applied.
import codfish from '@codfish/eslint-config';
export default defineConfig({
files: ['**/*.{js,jsx,ts,tsx}'],
extends: [codfish],
rules: {
// temporary
'@typescript-eslint/no-explicit-any': 'off',
},
});
Not using the defineConfig
function, just spread the config object:
import codfish from '@codfish/eslint-config';
export default [
...codfish,
{
// Your project-specific overrides
rules: {
// Override or add specific rules
'no-console': 'warn',
'@typescript-eslint/no-unused-vars': 'error',
},
},
];
Use the config without any overrides:
import codfish from '@codfish/eslint-config';
export default codfish;
Framework-specific customizations:
import { defineConfig } from 'eslint/config';
import codfish from '@codfish/eslint-config';
export default defineConfig(
codfish,
// Custom ignores
{
ignores: ['some-directory'],
},
{
files: ['**/*.spec.{js,ts,jsx,tsx}'],
rules: {
// Allow any in test files
'@typescript-eslint/no-explicit-any': 'off',
// Relax Testing Library rules if needed
'testing-library/prefer-screen-queries': 'warn',
},
},
{
files: ['**/*.config.{js,ts}'],
rules: {
// Allow require in config files
'@typescript-eslint/no-require-imports': 'off',
},
},
{
files: ['**/*.{js,jsx,ts,tsx}'],
rules: {
// Customize Next.js rules
'@next/next/no-img-element': 'warn',
'@next/next/no-html-link-for-pages': 'off',
},
},
);
Docker Configuration:
For projects leveraging Docker containers, you may want to disable import resolution errors for node_modules
if
packages are only available in the container but you're running the linter locally.
import codfish from '@codfish/eslint-config';
import docker from '@codfish/eslint-config/docker';
export default defineConfig(
codfish,
docker,
{
// Your app-specific overrides
},
);
dApps Configuration:
For decentralized applications that use build artifacts and blockchain globals, use the specialized dApp config. This config will set some globals as well as ignore missing build artifact imports. While you obviously need those to run your app, sometimes you might want to run the linter in a ci/cd environment and build artifacts might not be present.
import codfish from '@codfish/eslint-config';
import dapp from '@codfish/eslint-config/dapp';
export default defineConfig(
codfish,
dapp,
{
// Your app-specific overrides
},
);
The dApp configuration provides:
- Blockchain-specific globals (
artifacts
,contract
,web3
, etc.) - Import resolution handling for smart contract build artifacts
- Relaxed rules for generated contract files
This configuration includes some opinionated settings that you might want to customize for your project:
Prettier/Formatting:
- Semicolons: Enforces semicolons (
;
) - 120 character line width: Wider than the common 80/100 - you might prefer shorter lines
- 2-space indentation: Uses 2 spaces for tabs
- Single quotes: Prefers
'single'
over"double"
quotes - Trailing commas: Adds trailing commas everywhere
- Arrow parentheses: Avoids parens around single args (
x => x
not(x) => x
)
ESLint Rules:
- Import sorting: Enforces automatic import organization with specific grouping rules
- Lodash restrictions: Requires direct imports (
import get from 'lodash-es/get'
) instead of full lodash - React hooks deps: Disables
exhaustive-deps
rule - you might want this stricter - Console statements: Disallows
console.log
in regular code (only allowed in test files) - some teams prefer warnings instead of errors - Next.js rules: Enforces Next.js best practices and Core Web Vitals optimization
- Testing Library rules: Enforces Testing Library best practices in test files
File Ignores:
- Ignores common build directories (
.next
,coverage
,.vercel
, etc.) - Includes
.github
and.vitepress
folders (not ignored like most configs)
See the configuration examples below for instructions on overriding these settings to match your team's preferences.
Prettier is included and runs automatically through ESLint for JavaScript, TypeScript, JSX, and TSX files using the built-in configuration. You don't need to install or configure Prettier separately for basic usage.
You can then override the default config by creating your own Prettier config file, or extend the built-in config:
Option 1: Extend the built-in config (Recommended)
Create a prettier.config.js
file in your project root:
// prettier.config.js
import codfishConfig from '@codfish/eslint-config/prettier';
/**
* @see https://prettier.io/docs/en/configuration.html
* @type {import("prettier").Config}
*/
export default {
...codfishConfig,
// Override specific settings
printWidth: 80,
singleQuote: false,
tabWidth: 4,
trailingComma: 'none',
};
Option 2: Completely custom config
This config will completely override the built-in config.
// prettier.config.js
/**
* @see https://prettier.io/docs/en/configuration.html
* @type {import("prettier").Config}
*/
export default {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
trailingComma: 'all',
bracketSpacing: true,
bracketSameLine: false,
arrowParens: 'avoid',
proseWrap: 'always',
};
npm i -D eslint@9 @codfish/eslint-config prettier-plugin-tailwindcss
// prettier.config.js
import codfish from '@codfish/eslint-config/prettier';
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
export default {
...codfish,
plugins: ['prettier-plugin-tailwindcss'],
tailwindStylesheet: './src/styles/app.css',
tailwindFunctions: ['clsx'], // optional
};
Optionally, you can add these scripts to your package.json
for common linting workflows:
Basic scripts (no separate Prettier installation needed):
{
"scripts": {
"lint": "eslint .",
"fix": "eslint . --fix"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": ["eslint --fix"]
}
}
With Prettier installed separately (for formatting non-JS files):
{
"scripts": {
"lint": "eslint .",
"fix": "eslint . --fix",
"format": "prettier --config ./node_modules/@codfish/eslint-config/prettier.js --write \"**/*.{json,css,md}\"",
"check": "npm run lint && npm run format -- --check --no-write"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": ["eslint --fix"],
"*.{json,css,md}": ["prettier --write --config ./node_modules/@codfish/eslint-config/prettier.js"]
}
}
Extend from the shared codfish commitlint config.
import codfishConfig from '@codfish/eslint-config/commitlint.js';
export default Object.assign(codfishConfig, {
// your overrides here
rules: {
'scope-case': [1],
},
});
Or just reference it in your package.json:
{
"commitlint": {
"extends": ["./node_modules/@codfish/eslint-config/commitlint.js"]
}
}
Run commitlint in your CI to validate your commits:
Note
If you have @codfish/eslint-config as a dev dependency, and a commitlint config in your project, you can just call
npx commitlint
in your CI and it will use the shared config.
- You just need to setup node & install your dependencies before running commitlint.
- Don't forget to set the
fetch-depth
to0
to ensure commitlint can work properly.
# .github/workflows/validate.yml
on: pull_request_target
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
fetch-depth: 0 # Important for commitlint to work
- uses: actions/setup-node@v5
with:
node-version: lts/*
registry-url: https://registry.npmjs.org
- run: npm ci # or npm install
- run:
npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
--verbose
If you're upgrading from an older version that used Airbnb presets:
- Update to ESLint v9+:
npm install --save-dev eslint@9
- Switch to flat config: Replace
.eslintrc.js
witheslint.config.js
- Use import syntax: Change from
require()
toimport
statements - Remove explicit React config: React support is now automatically detected
- Update scripts: Ensure your lint script runs
eslint .
(flat config auto-discovery)