import { Tabs, Tab } from "../../../../../components/Tabs";
import { Callout } from '../../../../../components/Callout'

# ESLint in a monorepo

## Installing ESLint

Keeping a single version of ESLint is recommended for simplicity. Because of this, we suggest installing ESLint as `devDependency` at the root of your monorepo, or utilizing a tool like [syncpack](https://jamiemason.github.io/syncpack/) to maintain the same version across workspaces.

## Sharing config

Sharing an ESLint config across workspaces can be a boon to productivity by making all your workspaces more consistent.

Let's imagine a monorepo like this:

```
apps
├─ docs
│  ├─ package.json
│  └─ .eslintrc.js
└─ web
   ├─ package.json
   └─ .eslintrc.js
packages
└─ eslint-config-custom
   ├─ next.js
   ├─ library.js
   └─ package.json
```

We've got a package called `eslint-config-custom`, and two applications, each with their own `.eslintrc.js`.

### Our `eslint-config-custom` package

Our `eslint-config-custom` file contains two files, `next.js`, and `library.js`. These are two different ESLint configs, which we can use in different workspaces, depending on our needs.

Let's investigate the `next.js` lint configuration:

```js filename="packages/eslint-config-custom/next.js"
const { resolve } = require("node:path");

const project = resolve(process.cwd(), "tsconfig.json");

/*
 * This is a custom ESLint configuration for use with
 * Next.js apps.
 *
 * This config extends the Vercel Engineering Style Guide.
 * For more information, see https://github.com/vercel/style-guide
 *
 */

module.exports = {
  extends: [
    "@vercel/style-guide/eslint/node",
    "@vercel/style-guide/eslint/typescript",
    "@vercel/style-guide/eslint/browser",
    "@vercel/style-guide/eslint/react",
    "@vercel/style-guide/eslint/next",
    // turborepo custom eslint configuration configures the following rules:
    //  - https://github.com/vercel/turbo/blob/main/packages/eslint-plugin-turbo/docs/rules/no-undeclared-env-vars.md
    "eslint-config-turbo",
  ].map(require.resolve),
  parserOptions: {
    project,
  },
  globals: {
    React: true,
    JSX: true,
  },
  settings: {
    "import/resolver": {
      typescript: {
        project,
      },
    },
  },
  ignorePatterns: ["node_modules/", "dist/"],
  // add rules configurations here
  rules: {
    "import/no-default-export": "off",
  },
};
```

It's a typical ESLint config that extends the [Vercel styleguide](https://github.com/vercel/style-guide), nothing fancy.

The `package.json` looks like this:

```json filename="packages/eslint-config-custom/package.json"
{
  "name": "eslint-config-custom",
  "license": "MIT",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "@vercel/style-guide": "^4.0.2",
    "eslint-config-turbo": "^1.10.12"
  }
}

```

Note that the ESLint dependencies are all listed here. This is useful - it means we don't need to re-specify the dependencies inside the apps which import `eslint-config-custom`.

### How to use the `eslint-config-custom` package

In our `web` app, we first need to add `eslint-config-custom` as a dependency.

<Tabs items={['npm', 'yarn', 'pnpm']} storageKey="selected-pkg-manager">
  <Tab>
```jsonc filename="apps/web/package.json"
{
  "dependencies": {
    "eslint-config-custom": "*"
  }
}
```
  </Tab>
  <Tab>
```jsonc filename="apps/web/package.json"
{
  "dependencies": {
    "eslint-config-custom": "*"
  }
}
```
  </Tab>
  <Tab>
```jsonc filename="apps/web/package.json"
{
  "dependencies": {
    "eslint-config-custom": "workspace:*"
  }
}
```
  </Tab>
</Tabs>

We can then import the config like this:

```js filename="apps/web/.eslintrc.js"
module.exports = {
  root: true,
  extends: ["custom/next"],
};
```

By adding `custom/next` to our `extends` array, we're telling ESLint to look for a package called `eslint-config-custom`, and reference the file `next.js`.

<Callout type="info">
  You can avoid specifying the file by setting the entrypoint for your package. For example, setting `main: 'next.js'` in the `package.json`, could be loaded as simply `extends: ["custom"]` in your `.eslintrc.js`.
</Callout>

### Summary

This setup ships by default when you [create a new monorepo](/repo/docs/getting-started/create-new) with `npx create-turbo@latest`. You can also look at [our basic example](https://github.com/vercel/turbo/tree/main/examples/basic) to see a working version.

## Setting up a `lint` task

We recommend following the setup in the [`basics`](/repo/docs/handbook/linting#running-tasks) section, with one alteration.

Each `package.json` script should look like this:

```json filename="packages/*/package.json"
{
  "scripts": {
    "lint": "eslint ."
  }
}
```
