How to Create an ESLint Config Package in Turborepo
Saiful Islam

Saiful Islam @saiful7778

About: I am Saiful Islam, a front-end developer skilled in React.js, TypeScript, JavaScript, and Tailwind CSS. I build user-friendly web apps and have worked on Pocket School Quiz, DTR CLI, and DevMingle.

Location:
Lakshmipur, Bangladesh
Joined:
Mar 8, 2024

How to Create an ESLint Config Package in Turborepo

Publish Date: May 8
2 0

In a growing monorepo, maintaining consistent code quality across multiple apps and packages is crucial. One powerful way to do this is by creating a shared ESLint config package. In this post, I’ll walk you through how I set up a reusable ESLint config inside a Turborepo-based monorepo — perfect for full-stack projects using TypeScript, React, and Node.js.

✅ Related: How to Create a TypeScript Config Package in Turborepo


🧩 Step-by-Step: Creating the @dtr-cli/eslint-config Package

1. Create a Package Folder

Inside your packages folder, create a new directory for the ESLint config package:

mkdir -p packages/eslint-config
cd packages/eslint-config
npm init -y
Enter fullscreen mode Exit fullscreen mode

2. Update the package.json with a proper name and export map:

{
  "name": "@dtr-cli/eslint-config",
  "version": "0.0.1",
  "description": "Shared Eslint configuration for @dtr-cli",
  "license": "ISC",
  "author": "Saiful Islam <saiful.islam.rafi.88@gmail.com>",
  "exports": {
    "./base-eslint-config": "./base-eslint-config.js",
    "./backend-eslint-config": "./backend-eslint-config.js",
    "./react-eslint-config": "./react-eslint-config.js",
    "./frontend-eslint-config": "./frontend-eslint-config.js",
    "./extension-eslint-config": "./extension-eslint-config.js",
    "./ui-eslint-config": "./ui-eslint-config.js"
  },
  "files": [
    "base-eslint-config.js",
    "backend-eslint-config.js",
    "react-eslint-config.js",
    "frontend-eslint-config.js",
    "extension-eslint-config.js",
    "ui-eslint-config.js"
  ],
  "devDependencies": {
    "@eslint/js": "^9.26.0",
    "@tanstack/eslint-plugin-query": "^5.74.7",
    "eslint-config-prettier": "^10.1.3",
    "eslint-plugin-react": "^7.37.5",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "eslint-plugin-turbo": "^2.5.3",
    "globals": "^16.1.0",
    "typescript-eslint": "^8.32.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Create Base ESLint Config

base-eslint-config.js

This config includes basic settings for TypeScript, TurboRepo, and Prettier:

import eslintJs from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import turboPlugin from "eslint-plugin-turbo";
import tseslint from "typescript-eslint";

export const baseEslintConfig = [
  eslintConfigPrettier,
  eslintJs.configs.recommended,
  turboPlugin.configs["flat/recommended"],
  ...tseslint.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 2020,
    },
    rules: {
      "turbo/no-undeclared-env-vars": "warn",
    },
  },
];
Enter fullscreen mode Exit fullscreen mode

4. Create targeted ESLint config

🔧 backend-eslint-config.js
import globals from "globals";
import { baseEslintConfig } from "./base-eslint-config";

export const backendEslintConfig = [
  ...baseEslintConfig,
  {
    languageOptions: {
      globals: globals.node,
    },
  },
];
Enter fullscreen mode Exit fullscreen mode
⚛️ react-eslint-config.js
import reactEslint from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
import { baseEslintConfig } from "./base-eslint-config";

export const reactEslintConfig = [
  ...baseEslintConfig,
  reactEslint.configs.flat.recommended,
  reactHooks.configs["recommended-latest"],
  reactRefresh.configs.recommended,
  {
    settings: {
      react: { version: "19.1.0" },
    },
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    rules: {
      "react/react-in-jsx-scope": "off",
      "react/prop-types": "off",
      "react-refresh/only-export-components": [
        "warn",
        { allowConstantExport: true },
      ],
    },
  },
];
Enter fullscreen mode Exit fullscreen mode
🧩 frontend-eslint-config.js
import { reactEslintConfig } from "./react-eslint-config";

export const frontendEslintConfig = [...reactEslintConfig];
Enter fullscreen mode Exit fullscreen mode
🧪 extension-eslint-config.js
import { reactEslintConfig } from "./react-eslint-config.js";
import pluginQuery from "@tanstack/eslint-plugin-query";

export const extensionEslintConfig = [
  ...reactEslintConfig,
  ...pluginQuery.configs["flat/recommended"],
];
Enter fullscreen mode Exit fullscreen mode
🎨 ui-eslint-config.js
import { reactEslintConfig } from "./react-eslint-config.js";

export const uiEslintConfig = [...reactEslintConfig];
Enter fullscreen mode Exit fullscreen mode

Use the ESLint Config Package

In any project inside your monorepo, like apps/backend, create an eslint.config.mjs:

import { backendEslintConfig } from "@dtr-cli/eslint-config/backend-eslint-config";

/** @type {import("eslint").Linter.Config} */
export default [
  ...backendEslintConfig,
  {
    ignores: ["dist/**"],
  },
];
Enter fullscreen mode Exit fullscreen mode

apps/frontend create an eslint-config.js

import { frontendEslintConfig } from "@dtr-cli/eslint-config/frontend-eslint-config";

/** @type {import("eslint").Linter.Config} */
export default [...frontendEslintConfig];
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Having a centralized ESLint config package in your Turborepo setup is a massive productivity and consistency boost. Instead of rewriting and maintaining config files in every app, you can rely on one source of truth that scales with your repo.

This setup supports various contexts like backend, React apps, extensions, and UI libraries — making your developer experience smoother and your codebase cleaner.

Comments 0 total

    Add comment