Almost every React tutorial teaches the same folder structure:
src/
components/
hooks/
utils/
pages/
It looks clean. It works fine for a todo app. And it quietly falls apart the moment a real codebase grows past 30-40 components, because it organizes files by what they are instead of what they belong to.
The problem with organizing by type
Six months into a real project, components/ has 80 files in it. Half of them are only ever used by one feature. To find everything related to, say, the checkout flow, you're now searching across components/, hooks/, utils/, and maybe types/ — four folders, none of which tell you they're related.
The tell that this has gone wrong: deleting a feature means hunting through the whole codebase for its pieces instead of deleting one folder.
Organize by feature, not by type
The structure that actually holds up looks more like this:
src/
features/
checkout/
CheckoutForm.tsx
useCheckoutState.ts
checkout.types.ts
index.ts
productList/
ProductList.tsx
ProductCard.tsx
useProductFilters.ts
index.ts
shared/
components/ # Button, Card, Modal — genuinely reused everywhere
hooks/ # useDebounce, useLocalStorage — generic, feature-agnostic
lib/
Everything a feature needs — its components, its hooks, its types — lives together. When the feature goes away, so does the folder. When you're debugging checkout, you're not context-switching across four directories.
shared/ still exists, but the bar for putting something there is higher: it has to be used by more than one feature, and it has to be genuinely generic. A Button component belongs there. A useCheckoutValidation hook does not, no matter how "reusable" it feels in the moment.
The heuristic that actually matters
Forget the folder names for a second. The real question for any new file is:
If I delete the feature this belongs to, should this file disappear with it?
If yes, it goes inside the feature folder. If no — if three unrelated features would break — it goes in shared/. That one question resolves most of the "where does this go?" debates that otherwise turn into 20-minute Slack threads.
Don't over-engineer this on day one
None of this means you need features/, shared/, lib/, types/, and five levels of nesting for a project with six components. A flat structure is fine until it isn't. The mistake isn't picking the "wrong" structure early — it's refusing to restructure once the type-based folders start actively hurting you. Structure should scale with the codebase, not be decided once and defended forever.
The pattern I'd actually recommend: start flat, and the moment you catch yourself opening three folders to touch one feature, that's your signal to introduce feature folders — not before.