Tips & Tricks (Updated: 6/2/2026)

ESLint Flat Config With Claude Code for Real Projects

Set up ESLint Flat Config with Claude Code for TypeScript, React, Astro, CI checks, and practical fix prompts.

ESLint Flat Config With Claude Code for Real Projects

Do not let Claude Code invent the whole ESLint setup

ESLint is a static analysis tool for JavaScript and TypeScript. Static analysis means checking source code before the app runs, so the team can catch fragile patterns, unsafe async code, accessibility mistakes, and inconsistent imports before they reach review.

Since ESLint v9, Flat Config has been the standard configuration style, and the latest ESLint documentation in June 2026 still centers configuration around files such as eslint.config.js and eslint.config.mjs. The trap is that “please set up ESLint” is too vague for Claude Code. It may mix old .eslintrc habits, skip typed linting, or enable rules that make CI slow without improving product safety.

The better workflow is to give Claude Code a small contract: which stack you use, which files are generated, which command must pass, and which rules are allowed to be relaxed. This article uses the pattern Masa applies to a React admin app, an Astro content site, and a small TypeScript library.

Use the official docs as the source of truth: ESLint Configuration Files, typescript-eslint typed linting, eslint-plugin-astro User Guide, and the Claude Code CLI reference.

Install dependencies and scripts first

Before asking Claude Code to edit the repository, install the packages that match the project. The base set covers ESLint, TypeScript, React, hooks, accessibility, and import sorting. Add the Astro packages only when the project contains .astro files.

npm i -D eslint @eslint/js typescript typescript-eslint globals
npm i -D eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y
npm i -D eslint-plugin-simple-import-sort

# Add this only for Astro projects
npm i -D eslint-plugin-astro astro-eslint-parser

Then make verification explicit in package.json. lint:fix is for local cleanup. lint and typecheck are the commands CI should run. --max-warnings=0 turns warnings into CI failures; if the existing codebase is noisy, enable it after the first cleanup pass.

{
  "scripts": {
    "lint": "eslint . --max-warnings=0",
    "lint:fix": "eslint . --fix",
    "lint:debug": "eslint --print-config src/App.tsx > .eslint-debug.json",
    "typecheck": "tsc --noEmit",
    "ci:verify": "npm run lint && npm run typecheck"
  }
}

The workflow is simple, but the order matters. Claude Code should not stop after writing a config file; it should run the same commands that the team will run later.

package.json scripts
  -> eslint.config.mjs
  -> npm run lint:fix
  -> npm run lint
  -> npm run typecheck
  -> CI gate
  -> Claude Code review of the remaining diff

A Flat Config for TypeScript and React

For a React + TypeScript app, this eslint.config.mjs is a practical starting point. projectService: true enables typed linting, which lets rules understand TypeScript types instead of only syntax. Typed linting is useful, but it is not free, so generated folders and build output must be ignored early.

import js from "@eslint/js";
import globals from "globals";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import jsxA11y from "eslint-plugin-jsx-a11y";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import tseslint from "typescript-eslint";

export default tseslint.config(
  {
    ignores: [
      "node_modules/",
      "dist/",
      "build/",
      "coverage/",
      ".next/",
      ".astro/",
      "public/",
      "*.min.js"
    ]
  },
  js.configs.recommended,
  ...tseslint.configs.strictTypeChecked,
  ...tseslint.configs.stylisticTypeChecked,
  {
    files: ["**/*.{js,mjs,cjs,ts,tsx}"],
    languageOptions: {
      ecmaVersion: "latest",
      sourceType: "module",
      globals: {
        ...globals.browser,
        ...globals.node,
        ...globals.es2024
      },
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname
      }
    }
  },
  {
    files: ["**/*.{jsx,tsx}"],
    ...react.configs.flat.recommended,
    settings: {
      react: { version: "detect" }
    },
    rules: {
      ...react.configs.flat.recommended.rules,
      "react/react-in-jsx-scope": "off",
      "react/prop-types": "off"
    }
  },
  {
    files: ["**/*.{jsx,tsx}"],
    plugins: {
      "react-hooks": reactHooks,
      "jsx-a11y": jsxA11y
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      ...jsxA11y.configs.recommended.rules
    }
  },
  {
    plugins: {
      "simple-import-sort": simpleImportSort
    },
    rules: {
      "simple-import-sort/imports": "error",
      "simple-import-sort/exports": "error",
      "@typescript-eslint/consistent-type-imports": [
        "error",
        { prefer: "type-imports", fixStyle: "separate-type-imports" }
      ],
      "@typescript-eslint/no-floating-promises": "error",
      "@typescript-eslint/no-misused-promises": [
        "error",
        { checksVoidReturn: { attributes: false } }
      ],
      "@typescript-eslint/no-unused-vars": [
        "error",
        { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }
      ],
      "no-console": ["warn", { allow: ["warn", "error"] }]
    }
  },
  {
    files: ["**/*.{js,mjs,cjs}"],
    extends: [tseslint.configs.disableTypeChecked]
  },
  {
    files: ["**/*.{test,spec}.{ts,tsx}", "**/__tests__/**/*.{ts,tsx}"],
    rules: {
      "@typescript-eslint/no-explicit-any": "off",
      "@typescript-eslint/no-unsafe-assignment": "off",
      "@typescript-eslint/no-unsafe-member-access": "off"
    }
  }
);

This setup favors rules with real operational value. no-floating-promises catches forgotten awaits. no-misused-promises reduces unsafe async handlers in JSX. consistent-type-imports keeps type-only imports clear. The test override avoids weakening production code just because fixtures need a little flexibility.

Treat Astro files as their own surface

Astro files mix template markup, TypeScript frontmatter, component calls, and sometimes client-side framework islands. If you apply a React-only config to .astro files, linting often fails before it can find useful issues.

For an Astro content site, start with the plugin’s flat recommended configs, then add a small .astro override. The security-focused astro/no-set-html-directive rule is useful for blogs and docs sites because HTML injection is easy to miss during content review.

import js from "@eslint/js";
import astro from "eslint-plugin-astro";
import globals from "globals";
import tseslint from "typescript-eslint";

export default tseslint.config(
  {
    ignores: ["dist/", ".astro/", "node_modules/", "public/"]
  },
  js.configs.recommended,
  ...astro.configs["flat/recommended"],
  ...astro.configs["jsx-a11y-recommended"],
  ...tseslint.configs.recommendedTypeChecked,
  {
    files: ["**/*.{ts,tsx}"],
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node
      },
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname
      }
    }
  },
  {
    files: ["**/*.astro"],
    languageOptions: {
      parserOptions: {
        parser: tseslint.parser,
        extraFileExtensions: [".astro"],
        projectService: true,
        tsconfigRootDir: import.meta.dirname
      }
    },
    rules: {
      "astro/no-set-html-directive": "error",
      "astro/no-unused-define-vars-in-style": "error"
    }
  }
);

Do not use ESLint as a formatter for everything. Let Prettier handle formatting, then keep ESLint focused on unsafe code, accessibility, type-aware mistakes, and project conventions. The companion guide is Prettier configuration with Claude Code.

Three practical use cases

Use case 1 is a SaaS admin app. Inviting users, changing billing state, or updating roles often involves async code. In that context, @typescript-eslint/no-floating-promises should be an error because a missing await can turn into a real production incident.

Use case 2 is an Astro technical blog. The highest-value checks are .astro parsing, accessibility, unused styles, and unsafe HTML. The site may have many generated pages, so ignoring dist/, .astro/, and output folders is not optional.

Use case 3 is a TypeScript package. Public APIs need stricter rules than tests. Keep type imports consistent, prevent unused exports from drifting into the package, and relax any only in test fixtures or compatibility shims.

Project typeRules to tightenPlaces to relax
React admin appPromises, hooks, a11yStorybook mocks
Astro blog.astro, HTML injection, unused CSSGenerated content
TypeScript libraryType imports, unused vars, exportsTest fixtures

If you also want checks before commit, connect this with Husky and lint-staged for Claude Code. Keep pre-commit fast and leave the heavier typed linting to CI when the repository is large.

Add a CI gate

Local success does not mean team success. CI should run the same commands and fail on the same rule set. For GitHub Actions, this minimal workflow is enough for most projects.

name: code-quality

on:
  pull_request:
  push:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck

When an existing repository has hundreds of violations, do not run eslint --fix across the whole tree and ship one giant diff. First collect the failures by rule, then ask Claude Code to fix the highest-risk rules in small batches.

npm run lint
npm run lint:fix
npm run lint
npm run typecheck
npm run lint:debug

lint:debug writes the resolved config for one file. Use it when a rule appears in the wrong place, for example when a React rule touches an .astro file or a relaxed test rule leaks into production code.

Claude Code prompt templates

Template 1: new setup.

Add ESLint Flat Config to this repository.
The stack is TypeScript + React. Read package.json, tsconfig, and any existing lint setup first.
Create eslint.config.mjs and keep working until npm run lint and npm run typecheck pass.
Avoid behavior-changing autofixes. If one is necessary, explain why before applying it.

Template 2: fix lint errors.

I will paste the npm run lint output below.
Group the errors by rule, fix the lowest-risk categories first, and keep the diff small.
Use eslint-disable only as a last resort. If you use it, add a one-line reason.
After changes, run npm run lint and npm run typecheck.

Template 3: Astro support.

Review the ESLint setup for this Astro site.
Separate the config for .astro, .ts, and .tsx files. Use eslint-plugin-astro recommended flat config.
Set astro/no-set-html-directive to error and confirm React rules do not accidentally apply to the whole .astro file.

Template 4: CI failure investigation.

The GitHub Actions lint job is failing.
Compare local and CI versions of Node, npm, ESLint, and the ESLint plugins.
Create a reproduction command, then separate the fix into config change, dependency update, or source-code change.
Apply the smallest safe diff.

Claude Code works better when the prompt includes the files to inspect, the commands to run, and the rules of engagement. “Fix lint” is not enough. “Fix lint without weakening production rules, then prove it with these commands” is much safer.

Common failure modes

The first mistake is pasting an old .eslintrc extends chain into Flat Config. Some shareable configs still need compatibility handling, but prefer plugin docs that show flat examples.

The second mistake is enabling typed linting on every file the repository can see. projectService: true is powerful, but generated output, bundled files, and build artifacts can make CI slow. Put ignores in place before tuning rules.

The third mistake is shipping a massive autofix diff. Import sorting, type imports, unused variables, and formatting can bury real behavior changes. Masa usually splits the first rollout by rule category: promises first, imports second, framework-specific rules third.

The fourth mistake is leaving warnings forever. A warning that never blocks anything becomes background noise. Use error for policy, warn for temporary education, and no rule at all when the signal is weak.

The fifth mistake is hiding intent from Claude Code. Tell it that accessibility matters, that test fixtures may use any, or that public package APIs need stricter rules. Those constraints produce better configs than generic recommendations.

Summary

ESLint Flat Config makes the rule surface easier to see because the project configuration lives in one place. Claude Code is useful not because it can generate that file once, but because it can read the current codebase, run the checks, and adjust the setup without weakening the standards you care about.

Start by choosing the right baseline for React/TypeScript or Astro. Add npm run lint and npm run typecheck to CI. Then use Claude Code with explicit prompt templates, and review any eslint-disable or large --fix diff manually.

For the rest of the workflow, pair this with Prettier configuration and Husky + lint-staged. If you want reusable review prompts and rollout checklists, the ClaudeCodeLab products page has self-serve guides and consultation paths.

What I tested in practice

I tried this setup on a small React admin app and an Astro blog starter. The first pass surfaced import-order noise and several unhandled promises. A single eslint --fix diff was hard to review, so splitting the work by promises, imports, and Astro safety rules worked better. The most repeatable setup was to lock npm run lint and npm run typecheck into CI, paste failing logs into Claude Code, and ask for small fixes with proof commands attached.

#Claude Code #ESLint #code quality #TypeScript #development environment
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.