Back to React
React
easy
mid

How do you structure code and state management in a frontend application?

Feature-first folders, data/logic/presentation separation, and deliberate state placement: local by default, lift when shared, server-cache library for server state, Context for low-frequency globals, a store for complex global client state, URL for shareable state.

6 min read·~15 min to think through

This pairs two related decisions usually graded together in an exercise: how the code is organized and where state lives.

Code structure — feature-first

ts
src/
  features/
    <feature>/
      components/      # feature-specific UI
      hooks/           # feature data/logic hooks
      api/             # API calls for this feature
      types.ts
      index.ts         # public surface
  shared/
    ui/                # design-system primitives
    hooks/             # cross-cutting hooks
    lib/               # utils
  app/                 # routing, providers, layout
  • Group by feature, not file type — everything a feature needs is colocated; deleting a feature is deleting a folder.
  • Layer within a feature: data/logic hooks → presentational components → page composition.
  • Explicit public API (index.ts) so features don't import each other's internals.

State management — classify, then place

The skill is matching each kind of state to the cheapest mechanism that works:

State kindWhere it lives
Local UI (toggles, inputs, hover)useState/useReducer in the component
Shared by a subtreelifted to nearest common parent
Server dataReact Query / SWR (cache, dedup, refetch)
Low-frequency global (theme, user, locale)Context
Complex/high-frequency global client stateZustand / Redux Toolkit / Jotai (selectors)
Shareable / refresh-safe (filters, tab, page)URL params

Principles

  • Local-first — don't globalize state preemptively; most "global state" is just state lifted too far.
  • Server state ≠ client state — caching it in Redux means hand-rolling a cache; use a server-cache library.
  • Context re-renders all consumers — split contexts or use a selector store for hot values.
  • Derive, don't store — computed values via useMemo/selectors, not duplicated state.
  • Colocate — keep state near where it's used.

How they connect

Structure and state reinforce each other: feature folders own their data hooks; shared global state lives in app/ providers; presentational components stay stateless and reusable. A reviewer wants to see that both decisions were deliberate and consistent, not accidental.

Follow-up questions

  • Why feature-first over type-first folders?
  • How do you decide between Context and a store?
  • Why keep server state out of your client store?
  • What belongs in the URL rather than in component state?

Common mistakes

  • Type-first folders that don't scale.
  • Globalizing state that only one subtree uses.
  • Server data in a global store with hand-rolled loading flags.
  • Storing derived values instead of computing them.

Performance considerations

  • Context re-renders every consumer — split it or use selector-based stores for frequently-changing values. Server-cache libraries dedupe and avoid waterfalls. Colocation and small components keep memoization effective.

Edge cases

  • State shared by exactly two features — lift to shared or duplicate?
  • SSR/hydration — initial state must match server and client.
  • Optimistic updates blurring server/client state lines.
  • Cross-tab state synchronization.

Real-world examples

  • A feature folder owning useFeatureData (React Query) + presentational components; theme/auth in app-level Context; filters in the URL.

Senior engineer discussion

Seniors present structure and state as one coherent design: feature-first organization with layered concerns and explicit public APIs, plus state classified by kind and placed in the cheapest fitting mechanism. The senior signals are the server-vs-client distinction, local-first defaults, treating the URL as state, and deriving rather than storing.

Related questions