ESLint Cheat Sheet
ESLint 9 flat config: eslint.config.js structure, rule severity, plugins, ignores, CLI usage, and auto-fix workflows.
ESLint 9 ships the flat configuration format as the only supported style: a single eslint.config.js exporting an array of config objects. The legacy .eslintrc.* files still work but are deprecated, and the linter emits a warning at startup. New projects should start on flat config and never look back.
Flat config basics
A flat config is an ES module that exports either an array of config objects, a function that returns one, or — for libraries — a config object that callers can spread into their own array.
// eslint.config.js — flat config (ESLint 9+)
import js from "@eslint/js";
export default [
js.configs.recommended,
{
files: ["**/*.{js,jsx,ts,tsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
window: "readonly",
document: "readonly",
console: "readonly",
},
},
rules: {
"no-unused-vars": "warn",
"eqeqeq": "error",
"no-console": "warn",
},
},
];
| Concept | Role |
|---|---|
files | Globs that scope the config block |
languageOptions | Parser, ecma version, source type, globals |
plugins | Loaded plugin instances (flat style: object in, not string) |
rules | Map of rule id → severity or [severity, options] |
ignores | Globs the config does not apply to |
settings | Shared data accessible to rules |
Flat configs are applied in declaration order — later blocks override earlier ones for matching files, so put general rules first and project-specific overrides last.
Rules and severity
Severity is the first argument to a rule, either a string or a number. Numeric values are kept for the migration story but the named forms are clearer in new configs.
{
rules: {
// string severity
"no-unused-vars": "warn",
"no-console": "error",
// string + options
"no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
// numeric severity (legacy form, still supported)
"eqeqeq": 2,
// disable
"no-eval": "off",
},
}
| Severity | Meaning |
|---|---|
"off" / 0 | Rule disabled |
"warn" / 1 | Reported as a warning, exit code still 0 unless --max-warnings |
"error" / 2 | Reported as an error, non-zero exit code |
Some rules accept a second argument with options — always check the rule's docs; the wrong option shape is the most common source of "rule didn't apply" surprises.
Plugins and language options
Plugins register namespaces that can later appear in rules and configs. In flat config the plugin is an object, not a string, and the namespace is whatever the plugin exports under meta.name.
import js from "@eslint/js";
import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";
import reactPlugin from "eslint-plugin-react";
export default [
js.configs.recommended,
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
},
plugins: {
"@typescript-eslint": tsPlugin,
react: reactPlugin,
},
rules: {
"@typescript-eslint/no-unused-vars": "warn",
"react/jsx-key": "error",
},
},
];
| Need | Plugin / package |
|---|---|
| TypeScript parsing | @typescript-eslint/parser |
| TypeScript-aware rules | @typescript-eslint/eslint-plugin |
| React rules | eslint-plugin-react, eslint-plugin-react-hooks |
| Import ordering | eslint-plugin-import |
| A11y on JSX | eslint-plugin-jsx-a11y |
| Vue / Svelte | eslint-plugin-vue, eslint-plugin-svelte |
The meta.name of a plugin is what you use to prefix its rules in rules and in configs references; some plugins still document the npm package name and that mismatch is the #1 source of "rule not found" errors.
Useful rules to start with
These catch the majority of real bugs without becoming a style argument. Build a custom recommended once and reuse it across repos.
{
rules: {
// bugs
"no-undef": "error",
"no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"no-implicit-globals": "error",
"no-fallthrough": "error",
"no-return-await": "error",
"require-await": "warn",
// safety
"eqeqeq": ["error", "always", { null: "ignore" }],
"no-console": "warn",
"no-debugger": "error",
"no-eval": "error",
// style (often turned off per project)
"prefer-const": "warn",
"no-var": "error",
"object-shorthand": "warn",
},
}
| Category | Example rules |
|---|---|
| Likely bugs | no-undef, no-unused-vars, no-fallthrough, no-dupe-keys |
| Modernization | prefer-const, prefer-template, no-var, object-shorthand |
| Safety | eqeqeq, no-eval, no-implied-eval, no-new-func |
| Style | quotes, semi, indent, comma-dangle |
Ignoring files and sharing configs
In flat config, ignores lives inside any config object. Sharing a config means exporting an array (or a function that returns one) and having callers spread it.
{
ignores: [
"node_modules/**",
"dist/**",
"build/**",
"coverage/**",
".next/**",
"*.min.js",
],
}
To reuse logic across repos, export a function that takes options:
// eslint-config-myorg/index.js
import js from "@eslint/js";
export default function myorg(options = {}) {
return [
js.configs.recommended,
{
ignores: ["dist/**", "node_modules/**"],
},
{
rules: {
"no-console": options.allowConsole ? "off" : "warn",
},
},
];
}
Consumers spread the result into their own array:
import myorg from "eslint-config-myorg";
export default [
...myorg({ allowConsole: true }),
// local overrides
];
Running the CLI
npx eslint . lints everything matched by your configs; --fix repairs what it can; --max-warnings turns warnings into a non-zero exit when you want a strict CI gate.
# Lint everything in the project
npx eslint .
# Lint specific files or globs
npx eslint "src/**/*.ts"
# Auto-fix what can be fixed
npx eslint . --fix
# Fail the build when warnings exceed a threshold
npx eslint . --max-warnings 0
# Use a different config file
npx eslint . --config eslint.config.prod.mjs
# Inspect which config applies to a file
npx eslint --print-config src/index.ts
| Flag | Effect |
|---|---|
--fix | Apply automatic fixes where the rule supports them |
--max-warnings N | Exit non-zero when warnings > N |
--cache | Skip re-linting unchanged files |
--cache-location | Where to keep the cache file |
--print-config path | Dump the merged config for a file |
--debug | Trace rule loading and resolution |
--ext | Removed in flat config — use files in config |
For editor integration, install the official ESLint VS Code extension and the linter runs on save with no extra setup.