Linting & Code Formatting
Configure ESLint and Prettier for zero-friction code quality: flat config, shared configs, pre-commit hooks, and editor integration.
ESLint (linting) and Prettier (formatting) eliminate style debates and catch common errors automatically. When configured correctly, they run silently in the editor, on save, and in CI-producing consistent code without manual effort.
Prettier owns formatting-no ESLint formatting rules. ESLint catches logic and quality issues. Autofix everything fixable. Enforce via pre-commit hooks so nothing bad reaches the remote. Share configs across monorepo packages.
Workflow
- Configure ESLint flat config (v9+)ESLint flat config replaces `.eslintrc` - it's simpler, more explicit, and will be the only supported format going forward.
- Configure Prettier independentlyPrettier is opinionated and intentionally limited - fighting it wastes time. Accept its choices.
- Add lint-staged and Husky for pre-commit enforcementPre-commit hooks prevent linting errors from ever reaching the repository.
- Create shared configs for monoreposDuplicating ESLint/Prettier configs across packages leads to drift; a shared package enforces consistency.
- Integrate with editors and CIEditor integration provides real-time feedback; CI catches what slips through.
Output Format
ESLint flat config with TypeScript rules, Prettier config, lint-staged pre-commit hook, shared monorepo configs, CI linting gate, editor settings.
Examples
eslint.config.mjs: `export default tseslint.config(eslint.recommended, ...tseslint.recommendedTypeChecked, prettierConfig, { languageOptions: { parserOptions: { projectService: true } }, rules: { "@typescript-eslint/no-floating-promises": "error" } })`lint-staged: `{ "*.{ts,tsx}": ["eslint --fix --max-warnings=0", "prettier --write"], "*.{json,md,css}": "prettier --write" }`CI step: `- run: npx eslint . --max-warnings 0 && npx prettier --check .`Edge Cases & Gotchas
- ESLint and Prettier conflicts: always add eslint-config-prettier LAST; if conflicts remain, a rule is not covered by it - add it manually to ESLint config
- Auto-fix breaking logic: `--fix` can theoretically alter code semantics (rare); review auto-fixed changes in the pre-commit stage via `git diff --cached`
- Monorepo ESLint: each package needs its own `eslint.config.mjs` extending the shared config to pick up package-specific plugins (e.g., Next.js rules only in the Next.js app)
When to Break These Rules
- Disable specific rules for generated files (protobuf output, GraphQL types): add file patterns to `.eslintignore` or use `/* eslint-disable */` at the top of generated files.
Reference Files
ESLint flat config: https://eslint.org/docs/latest/use/configure/configuration-files-newtypescript-eslint: https://typescript-eslint.io/Prettier: https://prettier.io/Husky: https://typicode.github.io/husky/