Back to React
React
medium
mid

How would you organize React components for long term maintainability?

Organize by feature, not by file type. Separate presentational from container/logic concerns, extract reusable primitives into a shared UI layer, keep components small and single-responsibility, and colocate related files (component, styles, test, hooks).

6 min read·~10 min to think through

Maintainable component organization is about predictable structure, clear boundaries, and low coupling — so a new engineer can find and change things safely.

Folder structure: feature-first, not type-first

Avoid components/, hooks/, utils/ mega-folders that grow forever. Group by feature/domain:

ts
src/
  features/
    checkout/
      components/
      hooks/
      api/
      checkout-page.tsx
    catalog/
      ...
  shared/
    ui/        # design-system primitives: Button, Modal, Input
    hooks/     # truly cross-cutting hooks
    lib/       # utils
  app/         # routing, providers, layout

Everything a feature needs lives together; deleting a feature is deleting a folder.

Component layering

  1. UI primitives (shared/ui) — Button, Input, Modal. No business logic, fully reusable, well-typed props.
  2. Presentational components — receive data via props, render UI, raise events. Easy to test and Storybook.
  3. Container / feature components — fetch data, hold state, wire things up, compose presentational components.
  4. Pages / routes — layout and composition only.

Principles

  • Single responsibility — a component does one thing. If you can't name it cleanly, it's doing too much.
  • Colocation — keep component.tsx, component.test.tsx, component.module.css, and its local hook in the same folder.
  • Extract logic into hooksuseCheckoutForm, usePagination. Keeps components about rendering.
  • Composition over props explosion — pass children/slots instead of 15 boolean props. Compound components for related parts (<Tabs><Tabs.Tab/></Tabs>).
  • Stable public API — features expose a small index.ts; internals stay private. Don't import deep paths across features.
  • Keep them small — a 400-line component is a refactor signal.

Smells that mean reorganize

  • Props drilled through components that don't use them.
  • A "utils" or "components" folder nobody can navigate.
  • Circular imports between features.
  • The same UI rebuilt three times because no shared primitive exists.

Follow-up questions

  • Why feature-first over type-first folder structure?
  • How do you decide when to extract a hook vs keep logic inline?
  • What's the difference between presentational and container components today, with hooks?
  • How do you enforce that features don't import each other's internals?

Common mistakes

  • Type-first folders (components/, hooks/, utils/) that become unnavigable at scale.
  • Giant multi-hundred-line components doing fetching, state, and rendering.
  • Prop drilling instead of composition or context.
  • No shared UI layer, so the same button is reimplemented everywhere.

Performance considerations

  • Good boundaries enable code-splitting per feature/route. Small focused components make memoization targeted and effective. Colocated, well-bounded code reduces accidental re-renders from oversized shared components.

Edge cases

  • Code shared by exactly two features — promote to shared or keep duplicated?
  • Cross-feature navigation and shared layout.
  • Monorepo vs single app boundaries.
  • Gradual migration of a legacy type-first structure.

Real-world examples

  • A design system package (shared/ui) consumed by every feature, versioned and Storybooked.
  • Feature folders that map 1:1 to routes, each lazy-loaded.

Senior engineer discussion

Seniors frame this around change cost and team scaling: feature-first structure so ownership and deletion are clean, explicit public APIs (index.ts) and lint rules (eslint boundaries) to prevent cross-feature coupling, composition to avoid prop explosions. They treat the presentational/container split as a logic-vs-rendering separation enforced by custom hooks, not dogma.

Related questions