Frontend Patterns & Storybook
Component Architecture — Atomic Design
| Layer | Location | Examples | Data fetching |
|---|---|---|---|
| Atoms | components/ui/ | Button, Badge, Input, Spinner | None — pure props |
| Molecules | components/ui/ | ProductCard, PriceTag, DownloadButton | None — pure props |
| Organisms | components/features/ | ProductGrid, CartDrawer, CheckoutForm | Via TanStack Query |
| Pages | app/**/ | ProductsPage, AccountPage | Server Components |
Server vs Client Component Rule
Push
"use client"as deep as possible. Only interactive leaf components need it. TheProductCardis a Server Component — only theAddToCartButtoninside it is a Client Component. Default to Server, opt into Client.
Storybook
Storybook covers all atoms and molecules (components/ui/). These components are pure — they accept props and render. Storybook can render them with mock data without needing a running Medusa backend.
# Install
npx storybook@latest init # inside frontend/# Story convention
components/ui/Button/Button.tsx
components/ui/Button/Button.stories.tsx
components/ui/Button/Button.test.tsx # unit test alongsideState Management Rules
| State type | Tool | Rule |
|---|---|---|
| UI state (drawer open, loading) | Zustand | Never put server data in Zustand |
| Server data (products, orders) | TanStack Query | Stale-while-revalidate; invalidate on mutation |
| Cart | Medusa JS SDK | cart_id persisted in cookie, not localStorage |
| URL state (filters, pagination) | URL search params | Server-readable, shareable, bookmarkable |
| Auth token | HttpOnly cookie | Never accessible via JS, never localStorage |