Atomic design architecture
Atomic Design is a methodology by Brad Frost for building UI component systems - it splits the interface into five hierarchical levels: atoms, molecules, organisms, templates, and pages.
Theory
TL;DR
- Atoms are single UI elements (button, input, label). Molecules combine them for one task (search bar = input + button). Organisms build sections (header = logo + nav + search bar). Templates wireframe layouts. Pages inject real content.
- Main difference from plain component libraries: you build bottom-up, validating smaller parts before assembling larger ones.
- Use it when your team has 5+ devs/designers or your UI repeats across 10+ pages.
- Storybook is the standard tool to document and render each level in isolation.
Quick example
// Atom: single interactive element
const Button = ({ children }) => <button>{children}</button>;
// Molecule: combines atoms to solve one task
const SearchForm = () => (
<form>
<input placeholder="Search..." />
<Button>Go</Button>
</form>
);
// Organism: section built from molecules and atoms
const Header = () => (
<header>
<SearchForm />
</header>
);
// Result: consistent Header reused across every page without duplicationAtoms snap into molecules. Molecules snap into organisms. No duplication, no drift.
The five levels
Atoms are the smallest possible UI pieces: a button, an input, a label, a color token, a typographic style. They carry no business logic and no layout opinions.
Molecules group two or more atoms to complete one task. A search form (input + button) is a molecule. A form field (label + input + error message) is a molecule. One task, one unit.
Organisms combine molecules and atoms into a self-contained section. A site header with logo, navigation, and a search form is an organism. It stays identical whether it appears on a blog post or a checkout page.
Templates are page-level wireframes with slots for organisms. No real content, just structure. This is where designers and developers align before filling anything in.
Pages are templates with actual content. What the user sees. This is the only level where real data lives.
Key difference
Atomic Design enforces a build-from-the-bottom approach. You cannot ship a page without validating that it assembles from tested, documented parts. A traditional component library has no such constraint - you can build a one-off <BigSection> that duplicates half your design system without anyone noticing. Atomic hierarchy makes that obvious. Brad Frost's case studies document 40-60% fewer UI inconsistency bugs in teams that adopted this approach.
When to use
- Solo dev on a small app: skip it. Plain components work fine.
- Team of 5+ working on shared UI: start organizing by atoms and molecules at minimum.
- 50+ components across the system: full atomic hierarchy plus Storybook for documentation.
- Figma-to-code handoff: templates are your best tool here - they map directly to Figma frames.
- Refactoring a legacy app: start by extracting atoms. Audit repeated colors, spacing, and controls. Build the bottom layer first.
How it works internally
There is no runtime, no compiler step. Atomic Design is a process methodology. You organize files by level (atoms, molecules, organisms, templates, pages), document each level in Storybook, and enforce the hierarchy through code review and linting conventions. Storybook uses Webpack or Vite to render components in isolated iframes, so a designer can review a molecule without the full app running. PropTypes or TypeScript enforces prop contracts during development. The browser renders the final output the same way it always does.
Common mistakes
Styling molecules from outside
// Wrong: overriding internal spacing from a parent
<SearchMolecule style={{ margin: '10px' }} />
// Right: let the parent layout slot handle spacing
<div className="header-search-slot">
<SearchMolecule />
</div>Molecules should own their internal layout. Overriding margins from outside makes the same molecule look different across contexts. Use CSS variables or design tokens for spacing instead.
Putting data fetching inside organisms
// Wrong: organism fetches its own data
const UserCard = ({ userId }) => {
const user = useFetchUser(userId); // causes unpredictable re-renders
return <div>{user.name}</div>;
};
// Right: parent page passes data down
const UserCard = ({ name, avatar }) => (
<div>
<img src={avatar} alt={name} />
<span>{name}</span>
</div>
);Organisms that fetch data re-render unpredictably in lists and are hard to test in Storybook. Keep them as presentational components. Lift fetching to the page level.
Skipping templates and hardcoding layout in pages
// Wrong: layout lives inside the page component
const HomePage = () => (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 3fr' }}>
...
</div>
);
// Right: template separates structure from content
const TwoColumnTemplate = ({ sidebar, main }) => (
<div className="two-col">
<aside>{sidebar}</aside>
<main>{main}</main>
</div>
);Without templates, mobile and desktop layouts diverge. Each page ends up with its own grid system. Templates force you to agree on structure before filling it with content.
Over-atomizing semantic HTML
// Wrong: wrapping every heading tag as an atom
const H1Atom = ({ children }) => <h1>{children}</h1>;
// Right: use semantic tags directly; atomize interactive controls onlyWrapping every HTML tag as an atom adds indirection without benefit. Accessibility breaks when semantic elements get replaced with generic wrappers. Keep atoms to interactive and stylistic controls: buttons, inputs, badges, icons.
Real-world usage
- Storybook: the standard tool to isolate and document atoms/molecules, used by Shopify, GitHub, and most large-scale design systems.
- Chakra UI: molecules like
FormControlare built from atom-levelInputandLabelcomponents. - Material UI:
AppBaris an organism built from atomIconand moleculeToolbar. - PatternFly (Red Hat): a full atomic system for enterprise dashboards.
- Airbnb's design system follows atomic hierarchy internally, mapping design tokens to atoms.
Follow-up questions
Q: What is the difference between a molecule and an organism?
A: A molecule solves one narrow task (a labeled input, a search form). An organism combines multiple molecules and atoms into a self-contained section (a header with search, navigation, and a user menu). If a component can stand alone on a page as a recognizable section, it is likely an organism.
Q: How do templates differ from pages?
A: Templates are empty structural wireframes. Pages fill templates with real content and data. A template says "here goes a hero section, then a grid of cards"; a page says "the hero has this image and copy, the cards are these products."
Q: Why build your own system when libraries like Material UI exist?
A: Component libraries give you UI primitives, not your system. They lack your brand tokens, your specific business component patterns, and your team's conventions. Atomic Design lets you build a system that fits your product while still using third-party atoms as a base.
Q: How does Atomic Design scale to a team of 50 developers?
A: Through Storybook as a living style guide, TypeScript contracts on component props, and linting rules that enforce hierarchy. Changes to atoms propagate automatically because everything builds on them. A component reuse ratio above 70% is a realistic benchmark for a healthy system.
Q: Senior: How would you migrate a 100-page legacy app to Atomic Design?
A: Bottom-up. First, audit the codebase for repeated visual patterns: buttons, inputs, color values, spacing. Extract those as atoms. Then identify repeated UI groupings (form fields, cards) and move them to molecules. Use codemods to replace inline instances. Build Storybook docs in parallel. Do not refactor templates or pages until the lower two levels are stable. Measure progress by component reuse ratio, not by number of components. A/B test pages after migration to catch layout regressions.
Examples
Basic: search form assembled from atoms
// Atom
const Input = ({ placeholder }) => (
<input className="input" placeholder={placeholder} />
);
// Atom
const Button = ({ children }) => (
<button className="btn">{children}</button>
);
// Molecule: two atoms, one task
const SearchForm = () => (
<form className="search-form">
<Input placeholder="Search products..." />
<Button>Search</Button>
</form>
);
// SearchForm can appear in Header, Sidebar, or Modal
// without any change to the atoms themselvesTwo atoms, one molecule, zero duplication. The Input and Button components have no idea they are inside a search form. They accept props and render.
Intermediate: product card organism (e-commerce)
// Atom
const Button = ({ children }) => <button className="btn">{children}</button>;
// Molecule: handles price display with optional sale state
const Price = ({ amount, sale }) => (
<div className="price">
{sale && <span className="price--sale">${sale}</span>}
<span className={sale ? 'price--original' : ''}>${amount}</span>
</div>
);
// Organism: product card assembled from molecules and atoms
const ProductCard = ({ name, price, image, salePrice }) => (
<div className="card">
<img src={image} alt={name} />
<h3>{name}</h3>
<Price amount={price} sale={salePrice} />
<Button>Add to cart</Button>
</div>
);
// Used inside a product grid template
// Props validated via TypeScript or PropTypesProductCard is the organism. It does not know how many cards appear on the page, does not fetch data, and does not manage layout. The parent template handles all of that.
Advanced: template with responsive organism composition
// Template: page structure with slots for organisms
const DashboardTemplate = ({ sidebar, mainContent }) => (
<div className="dashboard">
<aside className="dashboard__sidebar">{sidebar}</aside>
<main className="dashboard__main">{mainContent}</main>
</div>
);
// Page: fills the template, handles responsive behavior
const AnalyticsPage = () => {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', check);
return () => window.removeEventListener('resize', check);
}, []);
return (
<DashboardTemplate
sidebar={isMobile ? null : <NavOrganism />}
mainContent={<ChartOrganism />}
/>
);
};
// Mobile: sidebar organism removed, no layout shift
// Desktop: full two-column layout
// Template stays unchanged - only the page decides what to renderThe template never changes. The page decides which organisms go into which slots. On mobile, the sidebar organism disappears entirely - the template just receives null in that slot. I have seen teams skip this pattern and end up with five slightly different grid implementations across five separate "mobile pages." One template fixes all of them.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.