Design Patterns

Best Practice FrontEnd React App Design Pattern

I've joined React projects where everything lived in a flat src/ folder. 200 files. No structure. Finding anything meant Cmd+P and guessing filenames. The...

24 Mar 2024

Best Practice FrontEnd React App Design Pattern

I've joined React projects where everything lived in a flat src/ folder. 200 files. No structure. Finding anything meant Cmd+P and guessing filenames. The first thing I do on any new React project is set up a folder structure that scales.

Here's the structure I've settled on after years of iteration.

Folder Structure (/src)

Text
components/     — Reusable UI components (Button, Modal, Card)
containers/     — Components that manage state and data fetching
pages/          — Top-level route components
services/       — API calls, auth helpers, external integrations
hooks/          — Custom React hooks
utils/          — Pure helper functions (formatting, validation)
constants/      — Shared constants and enums
config/         — Environment config, API endpoints
assets/         — Images, fonts, icons
styles/         — Global styles, theme variables, mixins
routes.ts       — Route definitions
App.ts          — Root component with layout and routing
index.ts        — Entry point

Principles That Actually Matter

Components are self-contained. A component owns its markup, styles, and tests. If I delete the folder, nothing else breaks.

Separate data from display. Containers fetch data and manage state. Presentational components receive props and render. This makes testing trivial and reuse natural.

State management fits the problem. Context API for simple shared state (theme, auth). Zustand or Redux for complex state with many consumers and frequent updates. Don't default to Redux — earn it.

API calls live in services. Never call fetch inside a component. Services handle HTTP, error normalization, and retry logic. Components call services through hooks.

Custom hooks encapsulate logic. useAuth(), useDebounce(), usePagination(). If a piece of logic could be reused across components, extract it into a hook.

Routes are centralized. One file defines all routes. Makes it easy to audit what pages exist and add route guards.

The Non-Negotiables

  • Error boundaries around every major section. A crash in the sidebar shouldn't take down the dashboard.
  • Accessibility from the start. Semantic HTML, keyboard navigation, ARIA attributes. Retrofitting accessibility is 10x harder.
  • Testing at every level. Unit tests for utils and hooks. Component tests with React Testing Library. E2E tests with Cypress or Playwright for critical flows.
  • Code splitting. Lazy load routes and heavy components. Your initial bundle should be small.
  • TypeScript. Non-negotiable on any project I lead. The cost of not having types shows up in every refactor.

Performance Patterns

  • Use React.memo, useMemo, and useCallback where profiling shows re-render problems — not everywhere preemptively.
  • Virtualize long lists with react-window or react-virtuoso.
  • Optimize images with next-gen formats and lazy loading.
  • Consider SSR or SSG (Next.js) for SEO-critical pages and faster initial loads.

The benefit: New developers find things fast. Code reviews are focused. Refactoring one layer doesn't ripple across the codebase. The structure communicates intent.

The cost: Upfront overhead. For a weekend prototype, this structure is overkill. Start simple, refactor into this shape when the project grows past 10-15 components. The structure should serve the team, not the other way around.