Lesson 9 of 9 · 18m
Organizing a React Codebase
Feature folders over type folders, colocating related code, and the boundaries that keep a growing app navigable.
The folder-layout debate has a clear answer at this point: organize by feature, not by file type. components/ + hooks/ + utils/ scales to maybe twenty features before finding anything becomes an archaeological dig.
Group by what ships together
src/
features/
billing/
InvoiceList.tsx
InvoiceCard.tsx
useInvoiceTotals.ts
invoice.api.ts
invoice.test.ts
checkout/
CheckoutForm.tsx
...
shared/
ui/ # buttons, inputs — genuinely app-agnostic
lib/ # date utils, formattersA feature folder contains everything for that feature: components, hooks, API calls, tests. Deleting a feature is deleting a directory. Extracting to shared/ happens only when something is used by two features — extracting on speculation creates a junk drawer.
Colocation beats the "clean" layout
Keep the test next to the file (invoice.test.ts), the query next to the component that uses it, the styles next to the markup. The "all types in types/" instinct scatters a feature across five directories — colocation means one screen shows you everything a feature touches.
Boundaries worth enforcing
- Shared imports features, features import shared — never the reverse. ESLint's
import/no-restricted-pathsor boundaries plugins can enforce it mechanically. - API layer per feature — the shape of your backend data is a feature concern; a global
api/folder becomes a god file. - Barrel files (
index.tsre-exports) are a footgun: they slow builds, break tree-shaking in some bundlers, and create import cycles. Import from the file, not the folder, except at deliberate public-API boundaries.
Conventions that pay
- One component per file, filename = component name.
- Colocate "closed" helper components in the same file as their only consumer — the file is the boundary.
- Keep route components thin — they wire data to feature components, they don't contain the feature.
Field notes
- The test of a structure: can a new hire find where a given UI lives by intuition? Feature folders pass; type folders fail.
- Refactoring is cheaper inside a feature folder — the blast radius is contained by design.