Skip to content

Modern React 29: ESLint, TypeScript, formatting, and CI gates

This is part 29 of the Modern React development series.

React projects stay easier to change when machines enforce the repeatable rules. TypeScript checks contracts, ESLint checks code patterns, formatters remove style debates, and continuous integration runs those checks before code lands.

Concept

A tooling gate is an automated check that must pass before code is accepted. In React, the most important gates catch invalid types, broken Hook rules, unsafe Effects, formatting drift, and failing tests.

Terms

  • ESLint: A JavaScript and TypeScript linting tool that reports code pattern issues.
  • CI: Continuous integration, an automated environment that runs checks for a change.
  • Formatter: A tool that rewrites code layout to a consistent style.
  • Type check: A TypeScript check that verifies types without producing runtime output.

Mental model

Think of tooling as guardrails on the road to review. Reviewers can focus on design and behavior because syntax, types, Hook rules, and formatting already had a machine pass.

How it is used

Use these gates in every React app that more than one person edits. They matter for Hooks, dependency arrays, component APIs, route types, generated code, design system consistency, and deploy confidence.

How to use it

  1. Run TypeScript with a command that fails on type errors.
  2. Enable React and Hook lint rules, including rules-of-hooks and exhaustive-deps.
  3. Run a formatter in check mode before review.
  4. Run unit, component, and selected browser tests in CI.
  5. Keep scripts named clearly, such as typecheck, lint, format:check, and test.

Example: Package scripts

{
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint .",
"format:check": "prettier --check .",
"test": "vitest run",
"test:e2e": "playwright test",
"ci": "npm run typecheck && npm run lint && npm run format:check && npm run test"
}
}

The command names describe what each gate verifies and make CI configuration easy to read.

Example: Effect lint value

import { useEffect } from "react";
export function RoomTitle({ roomId }: { roomId: string }) {
useEffect(() => {
document.title = "Room " + roomId;
}, [roomId]);
return <h1>Room {roomId}</h1>;
}
React output

Room general

The dependency list names the reactive value used by the Effect, which keeps the browser title synchronized with the current room.

Details to watch

  • Hook rules: React’s lint rules encode constraints that are easy to miss in review.
  • No silent skips: CI commands should fail the build when checks fail.
  • Generated files: Exclude generated output deliberately so gates check author-owned code.
  • Local speed: Fast local scripts make developers run checks before pushing.

Series navigation

References