- Auto fix for formatting (aimed to be used standalone without Prettier)
- Reasonable defaults, best practices, only one line of config
- Designed to work with TypeScript, JSX, etc. Out-of-box.
- Opinionated, but very customizable
- ESLint Flat config, compose easily!
- Automatic Angular, NGRX, TypeScript, Vitest support when the corresponding dependency is detected.
- Optional formatters support for formatting CSS, HTML, XML, etc.
- Style principle: Minimal for reading, stable for diff, consistent
- Sorted imports, dangling commas
- Single quotes, no semi
- Using ESLint Stylistic
- Respects
.gitignore
by default - Requires ESLint v9.21.0+
Warning
Please keep in mind that this is a personal config with a lot opinions. Changes might not always be pleased by everyone and every use cases.
If you are using this config directly, I'd suggest you review the changes everytime you update. Or if you want more control over the rules, always feel free to fork it. Thanks!
Run the command in your terminal:
pnpm add -D eslint @fabdeh/eslint-config
And create an eslint.config.mjs
in you project root:
// eslint.config.mjs
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig();
For example:
{
"scripts": {
"lint": "eslint . --fix",
"lint:ci": "eslint ."
}
}
🟦 VS Code support
Install VS Code ESLint extension
Add the following settings to your .vscode/settings.json
:
Since the beginning, we used ESLint Flat config. It provides much better organization and composition.
Normally you only need to import the defineConfig
function:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig();
And that's it! Or you can configure each integration individually, for example:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
// Enable stylistic formatting rules
// stylistic: true,
// Or customize the stylistic rules
stylistic: {
indent: 2, // 4, or 'tab'
quotes: 'single', // or 'double'
},
// Angular and NgRx are autodetected, you can also explicitly enable them:
angular: true,
ngrx: true,
// Disable vitest
vitest: false,
// `.eslintignore` is no longer supported in Flat config, use `ignores` instead
ignores: [
'**/fixtures',
// ...globs
],
});
The defineConfig
factory function also accepts any number of arbitrary custom config overrides:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig(
{
// Configure the fabdeh's config
},
// From the second arguments they are ESLint Flat Configs
// you can have multiple configs
{
files: ['**/*.ts'],
rules: {},
},
{
rules: {},
}
);
Going more advanced, you can also import fine-grained configs and compose them as you wish:
Advanced Example
We wouldn't recommend using this style in general unless you know exactly what they are doing, as there are shared options between configs and might need extra care to make them consistent.
// eslint.config.js
import {
angular,
comments,
ignores,
imports,
javascript,
jsdoc,
ngrx,
stylistic,
tailwindcss,
typescript,
unicorn,
vitest,
} from '@fabdeh/eslint-config';
import tseslint from 'typescript-eslint';
export default tseslint.config(
ignores(),
javascript(/* Options */),
comments(),
jsdoc(),
imports(),
unicorn(),
typescript(/* Options */),
stylistic(),
angular(),
ngrx(),
vitest(),
tailwindcss()
);
Check out the configs and factory for more details.
Thanks to antfu/eslint-config for the inspiration and reference.
All the rules are always bound to one or more file extensions (via minimatch pattern. i.e.: */.?([cm])[jt]s?(x) for all JS and TS file types including JSX syntax). If you want to override the rules, you need to specify the file extension:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig(
{
typescript: true,
vitest: true
},
{
// Remember to specify the file glob here, otherwise it might cause the vitest plugin to handle non-spec files
files: ['**/*.spec.?([cm])[jt]s', '**/*.test.?([cm])[jt]s'],
rules: {
'vitest/consistent-test-it': ['error', { fn: 'it' }],
}
},
{
// Without `files`, they are general rules for all files
rules: {
'@stylistic/semi': ['error', 'never']
}
}
);
We also provide the overrides
option in each integration to make it easier:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
typescript: {
overrides: {
'@typescript-eslint/consisten-type-definitions': ['error', 'interface'],
},
},
angular: {
tsOverrides: {
'@angular-eslint/prefer-signals': 'off',
},
htmlOverrides: {
'@angular-eslint/template/no-any': 'warn',
},
},
yaml: {
overrides: {
// ...
}
}
});
The following integrations are automatically enabled if the corresponding package is installed in your project:
Most of TypeScript rules are enable automatically if typescript
package is installed in you project. Some @typescript-eslint
rules are also enabled by default for JavaScript files.
You can explicitly enable/disable TypeScript integration manually:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
typescript: true,
});
The TypeScript integration also allow you to turn on/off rules that will report on using syntax that will not be allowed by TypeScript's --erasableSyntaxOnly option:
Recently, Node.js 23.6 unflagged experimental support for running TypeScript files directly; however, only certain constructs are supported under this mode.
...
TypeScript 5.8 introduces the
--erasableSyntaxOnly
flag. When this flag is enabled, TypeScript will only allow you to use constructs that can be erased from a file, and will issue an error if it encounters any constructs that cannot be erased.
You can enable these rules as follow:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
typescript: {
enableErasableSyntaxOnly: true,
},
});
These rules are disabled by default and therefore the associated ESLint plugin is also not installed by default.
Running pnpx eslint
should prompt you to install the required plugin; otherwise, you can install it manually:
pnpm add -D eslint-plugin-erasable-syntax-only
Angular support is detected automatically by checking if @angular/core
is installed in your project. You can also explicitly enable/disable it:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
angular: true,
});
NgRx support is also detected automatically if any of the following package is installed in your project.
@ngrx/store
@ngrx/effects
@ngrx/signals
@ngrx/operators
As the Angular integration is can be explicitly enabled/disabled:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
ngrx: true,
});
Of course, NgRx depends on Angular so the Angular integration will be enabled as well.
The vitest integration is detected automatically by checking if vitest
is installed in your project. It can be enable/disable manually in the configuration:
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
vitest: true,
});
We provide some optional integrations for specific use cases, that we don't include their dependencies by default.
Use external formatters to format files that ESLint cannot handle yet (.css
, .html
, etc). Powered by eslint-plugin-format
.
// eslint.config.js
import { defineConfig } from '@fabdeh/eslint-config';
export default defineConfig({
formatters: {
/**
* Format CSS, LESS, SCSS files
*/
css: true,
/**
* Format HTML files
*/
html: true,
/**
* Format Markdown files
*/
markdown: true,
}
});
Running npx eslint
should prompt you to install the required dependencies; otherwise, you can install them manually:
pnpm add -D eslint-plugin-format
If you want to apply lint and auto-fix before every commit, you can add the following to your package.json
:
{
"simple-git-hooks": {
"pre-commit": "pnpm nano-staged"
},
"nano-staged": {
"*": "eslint --fix"
}
}
and then
pnpm add -D nano-staged simple-git-hooks
# then, to activate the hooks
pnpm simple-git-hooks
This project follows Semantic Versioning for releases. However, since this is just a config and involves opinions and many moving parts, we don't treat rules changes as breaking changes.
- Node.js version requirement changes
- Huge refactors that might break the config
- Plugins made major changes that might break the config
- Changes that might affect most of the codebases
- Enable/disable rules and plugins (that might become stricter)
- Rules options changes
- Version bumps of dependencies
MIT License © 2025-PRESENT Fabien Dehopré