Jhune Carlo Trogelio3 min read

Atomic Design: How Brad Frost Changed the Way I Build Component Systems

How Brad Frost's Atomic Design methodology gave me a systematic approach to building scalable, maintainable UI component architectures — from atoms to full page templates.

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:

  1. Atoms — The smallest, indivisible UI elements
  2. Molecules — Simple groups of atoms functioning together
  3. Organisms — Complex UI sections composed of molecules and atoms
  4. Templates — Page-level structures that define layout without real content
  5. 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.