Flat ESLint config for JavaScript, TypeScript, Vue, React, and more.
- ESLint Flat config with reasonable but opinionated defaults
- Stylistic with single quotes, semi enabled, sorted imports, dangling commas,
- Aimed to be used without Prettier
- Designed to work with JSX, TypeScript, Vue, and React out of the box
- Applies Functional and Perfectionist by default
- Lints for JSDoc, JSON, RegEx, and YAML
- Every config can be enabled/disabled
- Respects
.gitignoreby default - Requires ESLint v9.10.0+
This section contains a list of the plugins used in the named configs that ships with this shareable config.
- ✅ Enabled by default
- ☑️ Enabled by auto-detect
- 🌟 Enabled unless lessOpinionated: false
- 🟨 Can be enabled manually
- 🎨 Stylistic rules enabled
- 💭 Type-aware rules available by setting the path to your tsconfig.json
- ℹ️ Accessibility rules available with a11y: true
| Plugin | 💼 | 🎨 | 💭 |
|---|---|---|---|
ESLint Comments |
✅ | ||
Functional |
✅ | 🎨 | |
Import Lite |
✅ | 🎨 | |
| ✅ | |||
JSDoc |
🟨 | 🎨 | |
JSONC |
🟨 | 🎨 | |
Node |
✅ | ||
Perfectionist |
🌟 | ||
Promises |
✅ | ||
| ☑️ℹ️ | 🎨 | 💭 | |
RegExp |
✅ | ||
Stylistic |
🌟 | 🎨 | |
| 🟨 | |||
TypeScript |
☑️ | 🎨 | 💭 |
Unicorn |
✅ | ||
| ☑️ℹ️ | 🎨 | 💭 | |
YAML |
🟨 | 🎨 |
I like to use Bun because it's hella fast. Thus all the install instructions are with Bun. If you use something else, check the syntax with your favorite package manager.
bun add -d eslint @moso/eslint-configCreate eslint.config.js in the root of your project:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso();Combine with legacy config:
If you still use some configs from the legacy eslintrc format, you can use the @eslint/eslintrc package to convert them to the flat config.
// eslint.config.js
import moso from '@moso/eslint-config';
import { FlatCompat } from '@eslint/eslintrc';
const compat = new FlatCompat()
export default moso(
{
ignores: [],
},
// Legacy config
...compat.config({
extends: [
'eslint:recommended',
// Other extends...
],
})
// Other flat configs...
);Note that
.eslintignoreno longer works in Flat config, see customization for more details.
For example:
{
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix"
}
}Since v1.0.0, this config has been migrated to ESLint Flat config. It provides much better organization and composition. And speed!
Normally you only need to import the moso preset:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso();Configure integrations by passing options to the main function:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso({
// Stylistic rules (enabled by default)
stylistic: false, // disable entirely
stylistic: {
indent: 2, // 4, or 'tab'
quotes: 'single', // or 'double'
},
// Auto-detected, or enable explicitly
typescript: true,
react: true,
vue: true,
// File format support
jsonc: false,
yaml: false,
// Replaces .eslintignore (no longer supported)
ignores: [
'**/dist',
'**/node_modules',
],
});Pass additional flat config objects as arguments. Rules are scoped to specific file types:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso(
{
// First argument: moso config options
vue: true,
typescript: true,
},
{
// Additional arguments: standard ESLint flat configs
files: ['**/*.vue'],
rules: {
'vue/multi-word-component-names': ['error', { ignores: [] }],
},
},
{
// You can pass multiple config objects
files: ['**/*.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
);The config returns a FlatConfigComposer object from eslint-flat-config-utils, enabling chainable methods:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso()
.prepend({
// Configs before the main config
})
.override('moso/javascript/rules', {
rules: {
'no-var': 'off',
},
})
.removeRules(
'no-console',
'no-debugger',
);Fine-grained config imports
You can import and compose individual configs directly. Only use this if you need granular control:
import {
combine,
comments,
ignores,
imports,
javascript,
jsdoc,
jsonc,
node,
sortPackageJson,
sortTsconfig,
stylistic,
typescript,
unicorn,
vue,
yaml,
} from '@moso/eslint-config';
export default combine(
comments(),
ignores(),
imports(),
javascript(/* options */),
jsdoc(),
jsonc(),
node(),
stylistic(/* options */),
typescript(/* options */),
unicorn(),
vue(/* options */),
yaml(),
);Framework support is auto-detected based on installed packages, but can be enabled explicitly. Dependencies are not bundled and will be prompted for installation.
Auto-detected if you have Gatsby, Next.js, Nextra, React, or Remix installed. Enable explicitly:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso({
react: true,
});Install dependencies when prompted, or manually:
bun add --dev @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh eslint-plugin-react-you-might-not-need-an-effectAuto-detected if you have Nuxt, VitePress, or Vue installed. Enable explicitly:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso({
vue: true,
});Install dependencies when prompted, or manually:
bun add --dev eslint-plugin-vue vue-eslint-parser eslint-processor-vue-blocks eslint-merge-processorsNote: Since Vue 2 has reached EOL, this config does not support Vue 2. If you need to support for Vue 2, you'll need to disable the imported configs from Vue 3, and replace them with the Vue 2 ones. You can see an inspiring example on eslint-plugin-vue. I recommend upgrading to Vue 3 if possible.
You can optionally enable typed linting. These are also known as "rules that require types", or simply "type-aware rules". This enables for much deeper insight into your code.
You enable them by passing the path of your tsconfig.json to the projectRoot option.
Warning
Enabling these rules will come with a slight performance cost, explained here.
To make things even more complicated, if you have enabled typed linting but disabled @stylistic, it will also disable the type-aware rules considered stylistic.
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso(
{
projectRoot: import.meta.dirname,
// If you wish to override any of these rules, use `overridesTypeAware`:
typescript: {
overridesTypeAware: {
'@typescript-eslint/no-deprecated': 'off',
},
},
},
);Note
TypeScript will still be auto-detected if you have the typescript-package installed, and any non-type-aware @typescript-eslint-rules will still be applied. Typed Linting is considered as an extra option.
Optionally, you can even disable type-aware rules on certain file extensions, or even specific files:
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso(
{
projectRoot: import.meta.dirname,
},
{
files: ['**/*.{js,jsx}'],
disableTypeAware: true,
},
{
files: ['**/components/User.jsx'],
disableTypeAware: true,
},
);Some rules are deemed as 'non-fixable' when inside your editor with ESLint integration:
Before v1.0.0, they used to be hard disabled. But with a helper, they are now just marked as 'non-fixable'. They are re-applied when you're linting through a terminal, or by using Lint Staged.
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso({
isInEditor: false,
});Linting and auto-fixing before every commit is easy, you just add the following to your package.json:
{
"simple-git-hooks": {
"pre-commit": "bunx lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
}and then
bun add -dev lint-staged simple-git-hooks
# to activate the hooks
bunx simple-git-hooksAntfu built a visual tool that can help you view what rules are enabled in your project: @eslint/config-inspector.
To view it in action, navigate to the root of your project that contains your eslint.config.js and run:
bunx @eslint/config-inspectorNo problem. I've extracted the things that I've deemed very opinionated in each integration, and made a setting that helps you disable all of it in one go.
// eslint.config.js
import moso from '@moso/eslint-config';
export default moso({
lessOpinionated: true,
});Note
The above will also disable functional and perfectionist completely. If you want to keep these enabled, you'll have to re-enable them explicitly, as demonstrated below
{
functional: true, // will default to 'lite' enforcement
// Optionally change the `functionalEnforcement`:
functionalEnforcement: 'recommended',
// Re-enable `perfectionist`
perfectionist: true,
}You can still use these to format files that aren't linted with this config, however, I strongly recommend you only format your code with ESLint, as Pretter and other AST-reading-then-reprint projects tend to ignore stuff like the original line breaks and might also cause inconsistent diffs when commiting code.
You will need to install and configure stylelint yourself, unfortunately.
I am actively considering adding linting support for TailwindCSS, however.
No worries, you can always override the rules locally in your project to fit your needs. If that doesn't cut it, you're welcome to fork this project and maintain your own config.
This ESLint config is heavily inspired by (and uses some of the same logic of):
- @antfu/eslint-config - @rebeccastevens/eslint-config - @eslint-sukka/eslint-config
Anthony Fu's config inspired me to take the journey, but this project has since evolved into a personal project. Especially Rebecca's config and strict(er) approach to coding has pushed me towards a stricter coding environment, with Functional and Azat's Perfectionist and De Morgan enabled.
A personal thank-you to Anthony, Rebecca, Sukka, and Azat, and everyone else who contributed to their projects.
Some rules are the same, however, there are some differences:
- My own opinionated rules added
- Stylistic, Perfectionist, and Functional enabled per default
- More freedom to disable every individual config, or only disable the opinionated parts
- Deprecated Vue 2 support
- Simplification in some areas
- Better type-aware checks
- Plugin memoization
- Optional a11y support for React and Vue
- No dangerous plugin renaming
BSD 3-Clause License
{ // Disable the default formatter, use eslint instead "prettier.enable": false, "editor.formatOnSave": false, // Auto fix "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.organizeImports": "never" }, // You can silent specific rules in you IDE, but still auto fix them "eslint.rules.customizations": [ { "rule": "@stylistic/*", "severity": "off", "fixable": true }, { "rule": "*-indent", "severity": "off", "fixable": true }, { "rule": "*-spacing", "severity": "off", "fixable": true }, { "rule": "*-spaces", "severity": "off", "fixable": true }, { "rule": "*-order", "severity": "off", "fixable": true }, { "rule": "*-dangle", "severity": "off", "fixable": true }, { "rule": "*-newline", "severity": "off", "fixable": true }, { "rule": "*quotes", "severity": "off", "fixable": true }, { "rule": "*semi", "severity": "off", "fixable": true } ], // Enable eslint for all supported languages "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "json", "jsonc", "yaml" ] }