What is component driven architecture and when would you use it?
Component-driven architecture builds UIs as compositions of small, independently developed, documented, and tested components. Build bottom-up: atoms → molecules → organisms → pages. Tools: Storybook for isolation + docs, design tokens for theming, a shared component library or design system. Benefits: reuse, consistency, parallel development, isolated testing. Costs: governance overhead, design-system maintenance, version coordination.
What it is
Component-driven architecture (CDA) treats components as the primary unit of the UI: build them in isolation, document them, test them, and assemble the app from them. Top-down (page first, components extracted) vs bottom-up (components first, pages composed).
CDA is bottom-up by design.
The pyramid (atomic design)
┌──────────┐
│ Pages │ <- routes, layouts
└──────────┘
┌──────────────┐
│ Templates │ <- layouts without data
└──────────────┘
┌──────────────────────┐
│ Organisms │ <- header, product card, sidebar
└──────────────────────┘
┌──────────────────────────────┐
│ Molecules │ <- search box, form field
└──────────────────────────────┘
┌──────────────────────────────────────┐
│ Atoms │ <- button, input, icon
└──────────────────────────────────────┘
┌────────────────┐
│ Design tokens │ <- color, space, type
└────────────────┘The terminology is from Brad Frost's Atomic Design. Variations exist (primitives / composed / patterns / pages); the layering principle is the point.
Why teams adopt it
- Reuse: Button defined once, used 200 times.
- Consistency: design language enforced at the leaf level.
- Parallel development: backend team can build a feature without waiting for design pages.
- Isolated testing: components testable without the whole app.
- Documented surface: Storybook + Docs makes the design vocabulary discoverable.
- Onboarding: new engineers learn the component library, not the whole app at once.
What makes a component 'good'
- Single purpose: does one thing, has a clear name.
- Predictable API: props are typed, sensible defaults, controlled or uncontrolled (pick one).
- Composable: accepts children, slots, or render props where it makes sense.
- Isolated: works in Storybook without app context.
- Themed via tokens: no hardcoded colors / spacing.
- Accessible: keyboard, screen reader, focus management.
- Documented: usage example, do/don't, variants.
The tooling
- Storybook / Ladle: render components in isolation, show variants, write docs.
- Chromatic / Playwright visual tests: visual regression on every PR.
- Design tokens: JSON / Style Dictionary for color, space, type, radii.
- Headless primitives: Radix UI, React Aria, Headless UI provide behavior without styling.
- Component test runner: Vitest + RTL or component-mode Playwright.
- Bundle analyzer: ensure the library is tree-shakeable per component.
Architectural decisions
Headless vs styled
- Headless (Radix, Headless UI): behavior + a11y, you bring styling. Maximum flex.
- Styled (MUI, Chakra, Mantine): batteries-included. Faster to ship, harder to theme deeply.
- Hybrid (shadcn/ui): copy code into your repo, customize freely.
Shared library vs per-app
- Shared library: published to internal registry. Best for multi-app orgs. Needs versioning, governance.
- Per-app: components live in the app repo. Simpler. Drift across apps.
Slot-based composition
<Card>
<Card.Header>Title</Card.Header>
<Card.Body>Content</Card.Body>
<Card.Footer>Actions</Card.Footer>
</Card>Better than prop bags for layouts. Mirrors HTML composition.
Anti-patterns
- Component soup: 200 atom buttons that look almost the same.
- Props explosion: a Card with 40 boolean props instead of 4 well-named variants.
- Coupling to app state: a Button that knows about Redux defeats reuse.
- No tokens: hardcoded colors everywhere, dark mode impossible.
- Story-less components: undocumented, undiscoverable.
- Visual drift: design system maintained by 1 person, the rest of the org bypasses it.
Versioning and governance
A shared component library is a contract. Treat it like one:
- SemVer strictly. Breaking changes get a major.
- Deprecation cycle, not silent removal.
- Codemods for breaking migrations.
- A design-system team or guild that owns the contract.
When it pays off
- 3+ apps sharing UI.
- Design system in active use across the org.
- Team big enough to need parallel work streams.
- Long-lived product where consistency matters.
When it's overkill
- Single-page MVP being shipped in a week.
- Team of 1-3 engineers.
- Product still finding PMF — overhead exceeds benefit.
Modern context: RSC and CDA
Server components push you toward 'server primitives' vs 'client primitives.' The CDA layering still applies but the boundary shifts: leaf components stay simple, but you start distinguishing zero-JS server components from interactive client islands.
Mental model
CDA is the application of single-responsibility + composition to UI. Build leaf components in isolation, theme them with tokens, document them in Storybook, version them as a contract, and compose apps top-down. The win is consistency + reuse + parallel velocity; the cost is governance overhead. Match the investment to the org's actual scale.
Follow-up questions
- •How do you decide between a shared library and per-app components?
- •When does atomic design break down?
- •How do design tokens fit into a component library?
- •How does CDA interact with React Server Components?
Common mistakes
- •Hardcoded colors / spacing instead of tokens — theming impossible.
- •Component prop explosion — 40 booleans instead of variants.
- •Coupling components to app state — kills reuse.
- •No Storybook — components are invisible to the team.
- •Adopting a heavyweight design system for a 3-person team.
Performance considerations
- •Tree-shakeable per-component exports keep bundle size honest. Heavyweight design systems can add 100KB+ if imported wholesale — set up per-component imports. Storybook adds CI cost; cache builds.
Edge cases
- •A11y baked in vs left to consumers — pick one.
- •Controlled vs uncontrolled — don't mix in the same component.
- •Polymorphic 'as' prop — flexible but hard to type.
- •Server vs client component boundary inside the library.
Real-world examples
- •Material UI — comprehensive styled library.
- •Radix UI — headless primitives, behavior + a11y only.
- •shadcn/ui — copy-in, fully customizable.
- •Atlassian Design System, Polaris, Carbon — large org-scale libraries.