First thing to note is that the eslint.config.js file should be at the root level of your project; you can target specific files and folders within the project structure from this one central configuration if need be i.e. in a monorepo.

The "extends" property does not exist under the new ESLint flat configuration schema, as per the 'Configuration Objects' specified in their documentation. You can directly import a configuration and declare it within the new eslint.config.js, however if that configuration extends other configurations this won't work - I can't see your '@ui' configuration definition so I have assumed in my code block that it does not extend configurations within itself (if it does, see blog link below on using @eslint/eslintrc 'compat' to bring in such configurations).

The "parserOptions" is also not a top-level property under the new schema and falls inside the 'languageOptions' property.

    // eslint.config.js at the project root
    import ParserTypescriptEslint from '@typescript-eslint/parser'
    import PluginImport from 'eslint-plugin-import';
    import PluginJest from 'eslint-plugin-jest';
    import UIConfig from 'ui-config-path';
    import globals from 'globals';
    
    export default [
      UIConfig,
      {
        files: [ "// Specify the files you wish to target with this configuration" ],
        languageOptions: {
          ecmaVersion: 2021,
          sourceType: 'module',
          globals: {
            ...globals.browser,
            ...globals.es2021,
          },
          parser: ParserTypescriptEslint,
          parserOptions: {
            project: ["./tsconfig.json"],
            tsconfigRootDir: __dirname,
          },
        },
        plugins: {
          import: PluginImport,
          jest: PluginJest
        },
        rules: {
          "jest/no-deprecated-functions": "off"
        },
        settings: {
          "import/resolver": {
            ...PluginImport.configs.typescript.settings['import/resolver'],
            typescript: {
              project: ["tsconfig.json"],
            },
          },
        },
        
      }
    ]

If you have multiple sub-configurations that you wish to use for different folders/systems - React, Express, Jest, etc - then you can write those configurations following the new schema and spread them into the main eslint.config.js file. This is a snippet of my main eslint.config.js that uses several different sub-configurations.

    import { EslintConfig, ConfigPrettier } from '@packages/eslint-config';
    import { EslintConfigReact } from '@packages/eslint-config-react';
    import { EslintConfigReactTest } from '@packages/eslint-config-react-test';
    import { EslintConfigExpress } from '@packages/eslint-config-express';
    
    export default [
      ConfigPrettier,
      {
        ignores: ['**/node_modules', '**/dist', '**/build', '**/__snapshots__', '**/mocks', '**/coverage'],
      },
      {
        // Client - React
        files: [
          'apps/*/frontend/**/*.ts',
          'apps/*/frontend/**/*.tsx',
          'apps/*/frontend/**/*.jsx',
          'apps/*/frontend/**/*.js',
        ],
        languageOptions: { ...EslintConfig.languageOptions, ...EslintConfigReact.languageOptions },
        plugins: { ...EslintConfig.plugins, ...EslintConfigReact.plugins },
        rules: { ...EslintConfig.rules, ...EslintConfigReact.rules },
        settings: { ...EslintConfig.settings, ...EslintConfigReact.settings },
      }
    ];

The following blog post was useful to me in setting up the new ESLint flat configuration in a monorepo structure: migration-eslint-to-flat-config

Answer from SS87 on Stack Overflow
🌐
ESLint
eslint.org › docs › latest › use › configure › configuration-files
Configuration Files - ESLint - Pluggable JavaScript Linter
If ignores is used without any ... configuration object. extends - An array of strings, configuration objects, or configuration arrays that contain additional configuration to apply....
🌐
ESLint
eslint.org › docs › latest › use › configure › migration-guide
Configuration Migration Guide - ESLint - Pluggable JavaScript Linter
You can also use the extends property to extend a shareable config. Shareable configs can either be paths to local config files or npm package names. In flat config files, predefined configs are imported from separate modules into flat config files.
🌐
ESLint
eslint.org › blog › 2025 › 03 › flat-config-extends-define-config-global-ignores
Evolving flat config with extends - ESLint - Pluggable JavaScript Linter
The defineConfig() function allows you to specify an extends array in any object, and that array can contain objects, arrays, or strings (for plugin configs that follow the recommended approach).
🌐
ESLint
eslint.org › docs › latest › extend › ways-to-extend
Ways to Extend ESLint - ESLint - Pluggable JavaScript Linter
This page explains the ways to extend ESLint, and how these extensions all fit together. Plugins let you add your own ESLint custom rules and custom processors to a project. You can publish a plugin as an npm module. Plugins are useful because your project may require some ESLint configuration that isn’t included in the core eslint package.
🌐
Prateeksurana
prateeksurana.me › blog › difference-between-eslint-extends-and-plugins
What is the difference between extends and plugins in ESLint config
Its job is to statically analyze and fix problematic patterns in your JavaScript code or code that doesn't adhere to some style guidelines that you would want to be followed throughout your whole project. The project owner can choose the rules they want to enforce via a configuration file .eslintrc.{js,yml,json} in the root directory of their project.
Top answer
1 of 1
9

First thing to note is that the eslint.config.js file should be at the root level of your project; you can target specific files and folders within the project structure from this one central configuration if need be i.e. in a monorepo.

The "extends" property does not exist under the new ESLint flat configuration schema, as per the 'Configuration Objects' specified in their documentation. You can directly import a configuration and declare it within the new eslint.config.js, however if that configuration extends other configurations this won't work - I can't see your '@ui' configuration definition so I have assumed in my code block that it does not extend configurations within itself (if it does, see blog link below on using @eslint/eslintrc 'compat' to bring in such configurations).

The "parserOptions" is also not a top-level property under the new schema and falls inside the 'languageOptions' property.

    // eslint.config.js at the project root
    import ParserTypescriptEslint from '@typescript-eslint/parser'
    import PluginImport from 'eslint-plugin-import';
    import PluginJest from 'eslint-plugin-jest';
    import UIConfig from 'ui-config-path';
    import globals from 'globals';
    
    export default [
      UIConfig,
      {
        files: [ "// Specify the files you wish to target with this configuration" ],
        languageOptions: {
          ecmaVersion: 2021,
          sourceType: 'module',
          globals: {
            ...globals.browser,
            ...globals.es2021,
          },
          parser: ParserTypescriptEslint,
          parserOptions: {
            project: ["./tsconfig.json"],
            tsconfigRootDir: __dirname,
          },
        },
        plugins: {
          import: PluginImport,
          jest: PluginJest
        },
        rules: {
          "jest/no-deprecated-functions": "off"
        },
        settings: {
          "import/resolver": {
            ...PluginImport.configs.typescript.settings['import/resolver'],
            typescript: {
              project: ["tsconfig.json"],
            },
          },
        },
        
      }
    ]

If you have multiple sub-configurations that you wish to use for different folders/systems - React, Express, Jest, etc - then you can write those configurations following the new schema and spread them into the main eslint.config.js file. This is a snippet of my main eslint.config.js that uses several different sub-configurations.

    import { EslintConfig, ConfigPrettier } from '@packages/eslint-config';
    import { EslintConfigReact } from '@packages/eslint-config-react';
    import { EslintConfigReactTest } from '@packages/eslint-config-react-test';
    import { EslintConfigExpress } from '@packages/eslint-config-express';
    
    export default [
      ConfigPrettier,
      {
        ignores: ['**/node_modules', '**/dist', '**/build', '**/__snapshots__', '**/mocks', '**/coverage'],
      },
      {
        // Client - React
        files: [
          'apps/*/frontend/**/*.ts',
          'apps/*/frontend/**/*.tsx',
          'apps/*/frontend/**/*.jsx',
          'apps/*/frontend/**/*.js',
        ],
        languageOptions: { ...EslintConfig.languageOptions, ...EslintConfigReact.languageOptions },
        plugins: { ...EslintConfig.plugins, ...EslintConfigReact.plugins },
        rules: { ...EslintConfig.rules, ...EslintConfigReact.rules },
        settings: { ...EslintConfig.settings, ...EslintConfigReact.settings },
      }
    ];

The following blog post was useful to me in setting up the new ESLint flat configuration in a monorepo structure: migration-eslint-to-flat-config

🌐
ESLint
eslint.org › docs › latest › extend
Extend ESLint - ESLint - Pluggable JavaScript Linter
This section explains how you can bundle and share ESLint configuration in a JavaScript package.
🌐
ESLint
eslint.org › docs › latest › use › configure
Configure ESLint - ESLint - Pluggable JavaScript Linter
Configuration Files - use a JavaScript file to specify configuration information for an entire directory and all of its subdirectories. This can be in the form of an eslint.config.js file which ESLint will look for and read automatically, or you can specify a configuration file on the command line.
Find elsewhere
🌐
ESLint
eslint.org › docs › latest › extend › shareable-configs
Share Configurations - ESLint - Pluggable JavaScript Linter
To use a shareable config, import the package inside of an eslint.config.js file and add it into the exported array using extends, like this:
🌐
Eslint
eslint.style › guide › config-presets
Shared Configurations | ESLint Stylistic
// Legacy config is no longer supported in v4+ // Please use v3.x if you need to use legacy config // We encourage you to migrate to flat config soon // .eslintrc.js module.exports = { extends: [ 'plugin:@stylistic/all-extends' ], rules: { // ...your other rules } }
🌐
GitHub
github.com › eslint › eslint › issues › 19116
Change Request: Flat config extends · Issue #19116 · eslint/eslint
November 7, 2024 - In flat configs, extends will not trigger any file loading. Instead, it will allow string identifiers for configs contained in plugins as well as objects. Here's a simple example: import js from "@eslint/js"; export default [ { plugins: { js }, files: ["**/*.js"], extends: ["js/recommended"], rules: { "prefer-const": "off" } } ];
Published   Nov 07, 2024
🌐
Raul Melo
raulmelo.me › en › blog › migration-eslint-to-flat-config
Embrace the Future: Navigating the New Flat Configuration of ESLint · Raul Melo
July 20, 2023 - You no longer need to prefix the lib with eslint-config-*, and you also no longer need to do a default export. Like extends, plugins will follow the same principle: we need to import and reference them inside one of one config object: ... import jsdoc from "eslint-plugin-jsdoc"; /** @type {import('eslint').Linter.FlatConfig[]} */ export default [ { files: ["**/*.js"], plugins: { jsdoc, }, rules: { // Other rules...
🌐
GitHub
github.com › eslint › eslint › discussions › 18131
Will the `"eslintConfig"` key in `package.json` files still work in ESLint v9? · eslint/eslint · Discussion #18131
The main reason is that it's not generally possible in ESLint v9, like it was possible in ESLint v8, to write a config file in JSON format. For example, there is no longer a standard to resolve a shared config by name by specifying "my-config-module" in the extends field.
Author   eslint
Top answer
1 of 4
148

extends uses a config file which applies set of rules when you add that to the extends options. A plugin on the other hand provides you with a set of rules that you can individually apply depending on your need. Just having a plugin does not enforce any rule. You have to choose which rules you need. A plugin may provide you with zero, one, or more configuration files. If the plugin provides configuration file, then you can load that in your extends section after adding the plugin in the plugins section.

So essentially, plugins given you some rules that have been coded and you can choose which ones are relevant. It may also provide config files to apply rules that the authors think are logically grouped/relevant but providing a config file is not mandatory for a plugin. extends, on the other hand, provides you the ability to apply rules in bulk based on config file specifications.

Example Plugin - eslint-plugin-react

{
  "plugins": [
    "react"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended"
  ]
}

Example Config - eslint-config-google

{
  "extends": [
    "google"
  ]
}
2 of 4
51

In addition to shmit's good answer:

extends

is about extending configurations in general, not only plugins. Potential values are:

  • "eslint:recommended"
  • "eslint:all"
  • Shareable configuration from npm package (eslint-config-xxx or scoped name)
  • Plugin configuration from npm package (eslint-plugin-xxx or scoped name)
  • Another configuration file, like "./my/path/.eslintrc.js"

Plugin notation: plugin:<package name>/<configuration name>, e.g. for eslint-plugin-react:

Copy "extends": ["plugin:react/recommended"]

By extending from a plugin config, we can get recommended rules without adding them manually.

plugins

A plugin is a special eslint npm package, that provides additional rule definitions (rules), environments, processors and configs for different configurations of recommended / default rule values.

The plugins property in .eslintrc.js is merely a flag to enable a given plugin after installation with npm i. We now can refer to the plugin's rules, but have to set all rules values manually.

Think of plugins as a way to activate a plugin - to use its rules, you need to add the plugin once in the chain in every case.

plugins is not needed in your own config, if it is already defined in a configuration, that you extend from by extends.

Example:

eslint-plugin-react already contains plugins: [ 'react' ], hence this entry is not needed anymore in own config and plugin rules can be used directly.

🌐
GitHub
gist.github.com › randallreedjr › 40282968b6f39dc3f423dd3cf1106455
ESLint config file extending local configuration · GitHub
ESLint config file extending local configuration. GitHub Gist: instantly share code, notes, and snippets.
🌐
Stack Overflow
stackoverflow.com › questions › 77061899 › how-to-extend-and-override-rules-config-in-eslint
How to extend and override rules config in ESLint?
{ "extends": ["next", "prettier"], "plugins": ["prettier", "@typescript-eslint", "unused-imports"], "rules": { "no-unused-vars": "off", "no-duplicate-imports": "off", "no-console": "warn", "no-return-await": "warn", "no-useless-return": "warn", "no-var": "warn", "react-hooks/exhaustive-deps": "off", "import/order": [ "warn", { "groups": ["type", "builtin", "external", "internal", "parent", "sibling", "index", "object"], "newlines-between": "always" } ], } } ./web.json (the config will be applied for web and extends from base.json)
🌐
Next.js
nextjs.org › docs › app › api-reference › config › eslint
Configuration: ESLint | Next.js
2 weeks ago - Learn how to use and configure the ESLint plugin to catch common issues and problems in a Next.js application.
🌐
GitHub
github.com › eslint › eslint › discussions › 18377
Add more comprehensive examples of best practise eslint.config.js's for v9 migration · eslint/eslint · Discussion #18377
Some changes were easy like moving ... under globals). However, with extends and plugins blocks I had no idea is importing them necessary anymore or what's the recommend best practise. It seems eslint:recommended was moved to "@eslint/js".configs.recommended so this added to the ...
Author   eslint
🌐
Expo Documentation
docs.expo.dev › integrations › tools › using eslint and prettier
Using ESLint and Prettier - Expo Documentation
2 weeks ago - To set up ESLint in your Expo project, you can use the Expo CLI to install the necessary dependencies. Running this command also creates a eslint.config.js file at the root of your project which extends configuration from eslint-config-expo.
🌐
ESLint
eslint.org › blog › 2022 › 08 › new-config-system-part-2
ESLint's new config system, Part 2: Introduction to flat config - ESLint - Pluggable JavaScript Linter
That one eslint.config.js file contains all of the configuration information for that run of ESLint so it dramatically reduces the disk access required as compared to eslintrc, which had to check each directory from the linted file location up to the root for any additional config files. Additionally, using a JavaScript file allowed us to rely on users to load additional information that their config file might need. Instead of extends ...