Turborepo

ESLint

ESLint is a static analysis tool for quickly finding and fixing problems in your JavaScript code.

Good to know:

This guide assumes you're using create-turbo or a repository with a similar structure.

This page is written for ESLint v8. If you'd like to contribute to add a version for v9, the core team is happy to review your pull request.

Installing ESLint

Install ESLint into each package where you'd like to run it:

Terminal
npm install eslint --workspace=web --workspace=docs --workspace=@repo/ui --save-dev

Good to know:

You can keep ESLint versions in sync using a tool like syncpack.

Sharing configuration

Sharing an ESLint config across packages makes their source code more consistent. Let's imagine a Workspace like this:

package.json
.eslintrc.js
package.json
.eslintrc.js

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

Our @repo/eslint-config package

Our @repo/eslint-config 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:

./packages/eslint-config/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: [
    require.resolve('@vercel/style-guide/eslint/node'),
    require.resolve('@vercel/style-guide/eslint/typescript'),
    require.resolve('@vercel/style-guide/eslint/browser'),
    require.resolve('@vercel/style-guide/eslint/react'),
    require.resolve('@vercel/style-guide/eslint/next'),
    // Turborepo custom eslint configuration configures the following rules:
    //  - https://github.com/vercel/turborepo/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 style guide, nothing fancy.

The package.json looks like this:

./packages/eslint-config/package.json
{
  "name": "@repo/eslint-config",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "@vercel/style-guide": "latest",
    "eslint-config-turbo": "latest"
  }
}

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

How to use the @repo/eslint-config package

In our web app, we first need to add @repo/eslint-config as a dependency.

./apps/web/package.json
{
  "dependencies": {
    "@repo/eslint-config": "*"
  }
}

We can then import the config like this:

./apps/web/.eslintrc.js
module.exports = {
  root: true,
  extends: ['@repo/eslint-config/next.js'],
};

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

Summary

This setup ships by default when you create a new monorepo with npx create-turbo@latest. You can also look at our basic example to see a working version.

Setting up a lint task

The package.json for each package where you'd like to run ESLint should look like this:

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

With your scripts prepared, you can then create your Turborepo task:

./turbo.json
{
  "tasks": {
    "lint": {}
  }
}

You can now run turbo lint with global turbo or create a script in your root package.json:

./package.json
{
  "scripts": {
    "lint": "turbo run lint"
  }
}

hours

Total Compute Saved
Get started with
Remote Caching →

On this page