Early in my career, every React project I worked on ended the same way: a components/ folder with 80 files, no naming convention, and a growing sense of dread every time someone said "just add a new component."
Button variants lived next to full page layouts. A Card component rendered differently in three places because three developers had three different ideas of what a "card" was. The design system was a Figma file that nobody kept in sync with the code.
Then I read Atomic Design by Brad Frost, and everything clicked.
The Core Idea
Brad Frost borrowed a metaphor from chemistry: just as all matter in the universe is composed of atoms that combine into molecules, which combine into organisms — all user interfaces can be broken down into a finite set of components that compose upward in predictable ways.
The five levels of Atomic Design:
- Atoms — The smallest, indivisible UI elements
- Molecules — Simple groups of atoms functioning together
- Organisms — Complex UI sections composed of molecules and atoms
- Templates — Page-level structures that define layout without real content
- Pages — Templates filled with real content, representing the final UI
This is not just a naming convention. It is a mental model for thinking about composition — and it fundamentally changed how I structure every frontend project.
Atoms: The Foundation
Atoms are the building blocks that cannot be broken down further without losing their meaning:
- A
<Button>component - A
<Label>component - An
<Input>component - An
<Avatar>component - A
<Badge>component
// atoms/Button.tsx
interface ButtonProps {
variant: 'primary' | 'secondary' | 'ghost'
size: 'sm' | 'md' | 'lg'
children: React.ReactNode
onClick?: () => void
}
export function Button({ variant, size, children, onClick }: ButtonProps) {
return (
<button
className={getButtonStyles(variant, size)}
onClick={onClick}
>
{children}
</button>
)
}
The key principle: atoms should have no knowledge of where they are used. A Button does not know if it lives in a login form or a checkout page. It just knows how to be a button.
Molecules: Purposeful Combinations
Molecules are simple groups of atoms that form a functional unit:
- A
<SearchField>=<Input>+<Button> - A
<UserInfo>=<Avatar>+<Label>+<Badge> - A
<FormField>=<Label>+<Input>+<ErrorMessage>
// molecules/SearchField.tsx
export function SearchField({ onSearch }: { onSearch: (query: string) => void }) {
const [query, setQuery] = useState('')
return (
<div className="flex gap-2">
<Input
value={query}
onChange={setQuery}
placeholder="Search..."
/>
<Button
variant="primary"
size="md"
onClick={() => onSearch(query)}
>
Search
</Button>
</div>
)
}
Molecules introduce behavior — the search field knows that the input and button work together. But they still do not know what page they live on or what data they search through.
Organisms: Complex Sections
Organisms are distinct sections of an interface, composed of molecules and atoms:
- A
<NavigationBar>= logo + nav links + search field + user menu - A
<ProductCard>= image + title + price + rating + add-to-cart button - A
<CommentThread>= comment list + reply form + pagination
// organisms/NavigationBar.tsx
export function NavigationBar() {
return (
<header className="flex items-center justify-between px-6 py-4">
<Logo />
<NavLinks items={['Home', 'Projects', 'Blog']} />
<SearchField onSearch={handleSearch} />
<UserMenu />
</header>
)
}
Organisms are where domain logic starts to appear. A NavigationBar knows about navigation. A ProductCard knows about products. This is intentional — the boundary between organisms and molecules is the boundary between generic and domain-specific.
Templates and Pages
Templates define the structural layout of a page without real content:
// templates/BlogPostTemplate.tsx
export function BlogPostTemplate({
header,
sidebar,
content,
footer,
}: TemplateProps) {
return (
<div className="min-h-screen">
{header}
<div className="grid grid-cols-12 gap-8">
<main className="col-span-8">{content}</main>
<aside className="col-span-4">{sidebar}</aside>
</div>
{footer}
</div>
)
}
Pages are templates filled with real data — they are the final rendered output. In a Next.js application, your page.tsx files are the "pages" level of Atomic Design.
Why This Matters in Practice
1. It Eliminates the "Where Do I Put This?" Problem
Before Atomic Design, every new component was a judgment call. Now there is a clear decision tree:
- Can it be broken down further? It is not an atom yet.
- Does it combine multiple atoms into a functional unit? It is a molecule.
- Does it represent a distinct section with domain logic? It is an organism.
- Does it define page structure without content? It is a template.
2. It Makes Design Systems Scalable
When I built the component library for D.Buzz, we had atoms that were reused across the entire platform — the same <Button>, <Avatar>, and <Badge> appeared in feeds, profiles, notifications, and settings. Changes to an atom propagated everywhere automatically.
3. It Aligns Designers and Developers
When both teams speak the same language — atoms, molecules, organisms — the handoff between Figma and code becomes almost mechanical. Designers build with the same primitives that developers implement.
4. It Prevents Component Duplication
The most common codebase problem I see: three different Card components built by three different developers because nobody established a shared vocabulary. Atomic Design eliminates this by making composition the default pattern.
The Folder Structure I Use
components/
atoms/
Button.tsx
Input.tsx
Avatar.tsx
Badge.tsx
Label.tsx
molecules/
SearchField.tsx
UserInfo.tsx
FormField.tsx
NavLink.tsx
organisms/
NavigationBar.tsx
ProjectCard.tsx
CommentThread.tsx
Footer.tsx
templates/
DashboardTemplate.tsx
BlogPostTemplate.tsx
AuthTemplate.tsx
Every developer on the team knows exactly where to find and where to create components. No ambiguity, no duplication, no 80-file flat folder.
Where Atomic Design Falls Short
No methodology is perfect. Here is where I have had to adapt:
- The naming can feel forced: Not every component fits cleanly into one level. A date picker is technically a molecule, but it feels more complex than that. I do not stress about perfect categorization — the mental model matters more than the labels.
- Over-atomization is a real risk: Breaking everything into the smallest possible atoms can lead to premature abstraction. If a component is only used once, it does not need to be an atom. Start with organisms and extract downward when you see reuse.
- It does not address state management: Atomic Design tells you how to structure your components, not how to manage data flow between them. You still need a clear state management strategy on top of it.
The Lasting Impact
Four years after reading Brad Frost's book, I still start every new frontend project by sketching out the atomic hierarchy. It has become second nature — a lens through which I see every interface as a composition of smaller, reusable parts.
The best part: it works regardless of the framework. I have applied Atomic Design in React, Vue, and even server-rendered templates. The principle is universal — composition scales, monoliths do not.
If you are building UI components and your components/ folder gives you anxiety, read Atomic Design. It might be the most practical frontend architecture book ever written.