eslint-9
ESLint 9 Flat Config Patterns
Overview
ESLint 9 uses flat config (eslint.config.js) as the default. The legacy .eslintrc.* format is deprecated.
Key differences from .eslintrc:
- Single config file, explicit imports — no magic
extendsstrings. - Config is an exported array of config objects, applied in order.
- Plugins are imported directly, not referenced by string name.
- Glob patterns in
filesreplace the oldoverridessystem.
Basic Setup
JavaScript
// eslint.config.js
import js from "@eslint/js";
export default [
js.configs.recommended,
{
rules: {
"no-unused-vars": "warn",
"no-console": "warn",
},
},
];
TypeScript
npm install -D eslint @eslint/js typescript-eslint
// eslint.config.ts
import js from "@eslint/js";
import tseslint from "typescript-eslint";
export default tseslint.config(js.configs.recommended, tseslint.configs.recommended, {
rules: {
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
},
});
typescript-eslint exports a config() helper that provides type safety and handles array flattening.
TypeScript Config Files
ESLint 9 supports eslint.config.ts natively — no extra packages needed. Use it for full type checking of your config:
import type { Linter } from "eslint";
defineConfig
ESLint provides defineConfig for type-safe configs without typescript-eslint:
import { defineConfig } from "eslint/config";
import js from "@eslint/js";
export default defineConfig([
js.configs.recommended,
{
files: ["**/*.js"],
rules: {
"no-console": "warn",
},
},
]);
defineConfig auto-flattens nested arrays and provides IntelliSense for rule names.
Adding Plugins
Plugins are imported and assigned to a namespace:
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
export default tseslint.config(js.configs.recommended, tseslint.configs.recommended, {
files: ["**/*.{tsx,jsx}"],
plugins: {
react: reactPlugin,
"react-hooks": reactHooksPlugin,
},
rules: {
"react/jsx-no-target-blank": "error",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
settings: {
react: { version: "detect" },
},
});
Many plugins now ship flat config presets. Check for plugin.configs["flat/recommended"] or similar exports.
Global Ignores
Use a config object with only ignores (no files) for global ignore patterns:
export default tseslint.config(
{ ignores: ["dist/", "node_modules/", "*.config.*", ".next/"] },
js.configs.recommended,
tseslint.configs.recommended,
);
Global ignores must be a standalone object — not combined with rules or files.
File-Specific Overrides
Use files globs to scope rules:
export default tseslint.config(
js.configs.recommended,
tseslint.configs.recommended,
// Stricter rules for source code
{
files: ["src/**/*.{ts,tsx}"],
rules: {
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-explicit-any": "error",
},
},
// Relaxed rules for tests
{
files: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
},
},
// Config files (CommonJS)
{
files: ["*.config.{js,cjs,mjs}"],
languageOptions: {
sourceType: "commonjs",
},
},
);
Prettier Integration
Use eslint-config-prettier to disable formatting rules that conflict with Prettier:
import prettierConfig from "eslint-config-prettier";
export default tseslint.config(
js.configs.recommended,
tseslint.configs.recommended,
prettierConfig, // must be last to override formatting rules
);
Don't use eslint-plugin-prettier — let Prettier handle formatting separately.
Common React + TypeScript Config
A complete config for a React + TypeScript project:
// eslint.config.ts
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
import prettierConfig from "eslint-config-prettier";
export default tseslint.config(
{ ignores: ["dist/", "node_modules/", ".next/"] },
js.configs.recommended,
tseslint.configs.recommended,
{
files: ["**/*.{tsx,jsx}"],
plugins: {
react: reactPlugin,
"react-hooks": reactHooksPlugin,
},
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
settings: {
react: { version: "detect" },
},
},
{
rules: {
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
"@typescript-eslint/consistent-type-imports": ["warn", { prefer: "type-imports" }],
},
},
prettierConfig,
);
Migration from .eslintrc
- Run the migration tool:
npx @eslint/migrate-config .eslintrc.json - Review the generated
eslint.config.mjs— the tool handles most conversions. - Replace string-based plugin references with direct imports.
- Replace
extendsstrings with imported config arrays. - Convert
overridesto separate config objects withfilesglobs. - Delete
.eslintrc.*and.eslintignorefiles.
Debugging
Inspect the resolved config for any file:
npx eslint --inspect-config
This launches an interactive viewer showing which rules apply to each file and where they come from.
More from grahamcrackers/skills
bulletproof-react-patterns
Bulletproof React architecture patterns for scalable, maintainable applications. Covers feature-based project structure, component patterns, state management boundaries, API layer design, error handling, security, and testing strategies. Use when structuring a React project, designing application architecture, organizing features, or when the user asks about React project structure or scalable patterns.
45react-aria-components
React Aria Components patterns for building accessible, unstyled UI with composition-based architecture. Covers component structure, styling with Tailwind and CSS, render props, collections, forms, selections, overlays, and drag-and-drop. Use when building accessible components, using react-aria-components, creating design systems, or when the user asks about React Aria, accessible UI primitives, or headless component libraries.
17clean-code-principles
Clean code principles for readable, maintainable TypeScript and React codebases. Covers naming, functions, abstraction, composition, error handling, comments, and code smells. Use when writing new code, refactoring, reviewing code quality, or when the user asks about clean code, readability, or maintainability.
10typescript-best-practices
Core TypeScript conventions for type safety, inference, and clean code. Use when writing TypeScript, reviewing TypeScript code, creating interfaces/types, or when the user asks about TypeScript patterns, conventions, or best practices.
9tanstack-query
TanStack Query v5 patterns for server state management, caching, mutations, optimistic updates, and query organization. Use when working with TanStack Query, React Query, server state, data fetching hooks, or when the user asks about caching strategies, query invalidation, or mutation patterns.
8zustand
Zustand state management patterns for React including store design, selectors, slices, middleware (immer, persist, devtools), and async actions. Use when managing client-side state, creating stores, working with Zustand, or when the user asks about global state management, store patterns, or state persistence.
7