Component Architecture At Scale
Design React component systems that stay maintainable as your app and team grow.
Designing Stable Boundaries
Organize code by feature slices (users, billing, dashboard) rather than technical type (components, hooks, utils). Each slice owns its UI, hooks, API calls, and tests. Shared primitives live in a design system layer consumed by features.
Clear boundaries localize change — a billing redesign should not require editing user profile internals. Code review ownership maps naturally to feature directories.
- Feature-first folder structure
- Design system for shared primitives
- Localized change impact
Contract-Driven Components
Treat props as versioned APIs. Document breaking prop changes in changelogs. Prefer composition (`children`, render props, slots) over configuration props that multiply with every new variant.
TypeScript interfaces enforce contracts at compile time. Storybook documents visual variants and interaction states for design-dev alignment.
- Props as stable public API
- Composition over config explosion
- Storybook for visual contracts
Dependency Direction
Features depend inward on shared layers, never sideways on sibling features. Cross-feature communication goes through shared services, URL routing, or global events — not direct imports of another feature's internals.
Enforce with ESLint boundary rules (eslint-plugin-boundaries) or Nx module constraints. Violations caught in CI prevent entanglement that blocks parallel team work.
- No sideways feature imports
- Shared services for cross-feature needs
- Lint rules enforce boundaries
State Ownership at Scale
Colocate state with the feature that owns it. URL state for shareable UI state (filters, tabs). Server cache (TanStack Query) for remote data. Context or stores only for truly cross-cutting session data.
Avoid a single global Redux store for everything — slice stores by domain or use query cache as source of truth for server data.
- URL for shareable UI state
- Query cache for server data
- Avoid monolithic global stores
Migration and Technical Debt
Strangle legacy patterns incrementally — wrap old components behind new interfaces rather than big-bang rewrites. Codemods automate prop renames and import path updates across large codebases.
Document architectural decision records (ADRs) so new engineers understand why boundaries exist. Architecture erodes without intentional maintenance — schedule regular dependency audits and dead code removal.
- Incremental strangler migrations
- Codemods for mechanical refactors
- ADRs document decisions