Skip to the content.
Agent Skill Available Download this Agent Skill (SKILL.md) to drop into ~/.claude/skills/ or any Agent-Skills-compatible runtime for AI-assisted Atomic Design work.
Download
atomic-design

Atomic Design

Key Insight

Atomic Design isn’t a step-by-step process (“first make all atoms, then molecules…”)—it’s a mental model for thinking about UI hierarchy. The chemistry metaphor helps teams communicate: “This button is an atom, this search form is a molecule (label + input + button), this header is an organism (logo molecule + navigation molecule + search molecule).” The power comes from seeing both the forest and the trees simultaneously: you can drill down from a page to its template to its organisms to its molecules to its atoms, or build up from atoms to pages. This prevents the common trap of designing one-off pages with no reusable components. The five levels (atoms, molecules, organisms, templates, pages) aren’t strict rules—some teams add ions (design tokens) or ecosystems (multi-page flows)—they’re a shared language for discussing modularity.

Detailed Description

Atomic design is a methodology for creating interface design systems, inspired by chemistry and the natural world. It consists of five distinct stages that work together to create a hierarchical and deliberate approach to UI design.

The Five Levels:

  1. Atoms: The basic building blocks of interfaces
    • HTML elements like <button>, <input>, <label>, <h1>
    • Abstract elements like colors, fonts, spacing values
    • Cannot be broken down further without losing meaning
    • Examples: Button, input field, icon, color swatch, typography style
  2. Molecules: Simple groups of UI elements functioning together as a unit
    • Combination of atoms that form a functional unit
    • Single responsibility: do one thing well
    • Examples: Search form (label + input + button), card header (avatar + name + timestamp), form field (label + input + error message)
  3. Organisms: Relatively complex components composed of groups of molecules and/or atoms
    • Form distinct sections of an interface
    • Standalone, reusable components
    • Examples: Header (logo + navigation + search), product card (image + title + description + price + button), comment thread (multiple comment molecules)
  4. Templates: Page-level objects that place components into a layout
    • Articulate the design’s underlying content structure
    • Focus on content structure, not final content
    • Show component placement and relationships
    • Examples: Homepage template (hero + features grid + testimonials + footer), blog post template (header + sidebar + content area), dashboard template (nav + stats widgets + charts)
  5. Pages: Specific instances of templates with real representative content
    • Show what the UI actually looks like with real content
    • Test templates with varying content lengths
    • Examples: Homepage with actual product data, blog post about “Atomic Design”, user dashboard for “Alice”

Key Principles:

Code Examples

Basic Example: Atoms → Molecules → Organisms

<!-- ===== ATOMS ===== -->
<!-- Basic building blocks that can't be broken down further -->

<!-- Atom: Button -->
<button class="btn btn-primary">Click me</button>
<button class="btn btn-secondary">Cancel</button>

<!-- Atom: Input -->
<input type="text" class="input" placeholder="Enter text">

<!-- Atom: Label -->
<label class="label">Email</label>

<!-- Atom: Icon -->
<svg class="icon icon-search">
  <use href="#icon-search"></use>
</svg>

<!-- Atom: Heading -->
<h2 class="heading heading-lg">Welcome</h2>


<!-- ===== MOLECULES ===== -->
<!-- Simple groups of atoms functioning together -->

<!-- Molecule: Form Field (label + input + error) -->
<div class="form-field">
  <label class="label" for="email">Email</label>
  <input type="email" class="input" id="email">
  <span class="error-message">Please enter a valid email</span>
</div>

<!-- Molecule: Search Form (label + input + button) -->
<form class="search-form">
  <label class="label sr-only" for="search">Search</label>
  <input type="search" class="input" id="search" placeholder="Search...">
  <button type="submit" class="btn btn-primary">
    <svg class="icon"><use href="#icon-search"></use></svg>
    Search
  </button>
</form>

<!-- Molecule: Card Header (avatar + name + timestamp) -->
<div class="card-header">
  <img src="avatar.jpg" alt="User avatar" class="avatar">
  <div class="user-info">
    <h3 class="heading heading-sm">Alice Johnson</h3>
    <time class="timestamp">2 hours ago</time>
  </div>
</div>


<!-- ===== ORGANISMS ===== -->
<!-- Complex components made of molecules and atoms -->

<!-- Organism: Site Header (logo + navigation + search) -->
<header class="site-header">
  <!-- Logo molecule (image + text) -->
  <div class="logo">
    <img src="logo.svg" alt="Company Logo" class="logo-image">
    <span class="logo-text">MyApp</span>
  </div>
  
  <!-- Navigation molecule (list of links) -->
  <nav class="main-nav">
    <a href="/home" class="nav-link">Home</a>
    <a href="/products" class="nav-link">Products</a>
    <a href="/about" class="nav-link">About</a>
  </nav>
  
  <!-- Search molecule -->
  <form class="search-form">
    <input type="search" class="input" placeholder="Search...">
    <button type="submit" class="btn btn-primary">Search</button>
  </form>
</header>

<!-- Organism: Product Card -->
<article class="product-card">
  <!-- Image atom -->
  <img src="product.jpg" alt="Product name" class="product-image">
  
  <!-- Card content molecules -->
  <div class="product-info">
    <h3 class="heading heading-md">Product Name</h3>
    <p class="product-description">Product description text here.</p>
    
    <!-- Price molecule (label + price) -->
    <div class="product-price">
      <span class="price-label">Price:</span>
      <span class="price-value">$99.99</span>
    </div>
    
    <!-- Button atom -->
    <button class="btn btn-primary">Add to Cart</button>
  </div>
</article>

Practical Example: Building a Complete Design System

// ===== DESIGN TOKENS (Sub-atomic level) =====
// Values that feed into atoms

const designTokens = {
  colors: {
    primary: '#0066cc',
    secondary: '#6c757d',
    success: '#28a745',
    danger: '#dc3545',
    gray100: '#f8f9fa',
    gray900: '#212529'
  },
  spacing: {
    xs: '0.25rem',  // 4px
    sm: '0.5rem',   // 8px
    md: '1rem',     // 16px
    lg: '1.5rem',   // 24px
    xl: '2rem'      // 32px
  },
  typography: {
    fontFamily: {
      base: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto',
      heading: 'Georgia, serif'
    },
    fontSize: {
      sm: '0.875rem',  // 14px
      base: '1rem',    // 16px
      lg: '1.125rem',  // 18px
      xl: '1.5rem',    // 24px
      '2xl': '2rem'    // 32px
    },
    fontWeight: {
      normal: 400,
      medium: 500,
      bold: 700
    }
  },
  borderRadius: {
    sm: '0.25rem',
    md: '0.5rem',
    lg: '1rem',
    full: '9999px'
  }
};


// ===== ATOMS =====
// React component examples

import React from 'react';

// Atom: Button
function Button({ variant = 'primary', size = 'md', children, ...props }) {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      {...props}>
      {children}
    </button>
  );
}

// Atom: Input
function Input({ type = 'text', ...props }) {
  return (
    <input
      type={type}
      className="input"
      {...props}
    />
  );
}

// Atom: Label
function Label({ htmlFor, children }) {
  return (
    <label htmlFor={htmlFor} className="label">
      {children}
    </label>
  );
}

// Atom: Heading
function Heading({ level = 2, size = 'md', children }) {
  const Tag = `h${level}`;
  return <Tag className={`heading heading-${size}`}>{children}</Tag>;
}


// ===== MOLECULES =====

// Molecule: Form Field (label + input + error)
function FormField({ label, id, error, ...inputProps }) {
  return (
    <div className="form-field">
      <Label htmlFor={id}>{label}</Label>
      <Input id={id} aria-invalid={!!error} {...inputProps} />
      {error && <span className="error-message" role="alert">{error}</span>}
    </div>
  );
}

// Molecule: Search Form
function SearchForm({ onSearch, placeholder = 'Search...' }) {
  const [query, setQuery] = React.useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    onSearch(query);
  };
  
  return (
    <form className="search-form" onSubmit={handleSubmit}>
      <Label htmlFor="search" className="sr-only">Search</Label>
      <Input
        type="search"
        id="search"
        placeholder={placeholder}
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      <Button type="submit" variant="primary">Search</Button>
    </form>
  );
}

// Molecule: User Badge (avatar + name)
function UserBadge({ user }) {
  return (
    <div className="user-badge">
      <img src={user.avatar} alt="" className="avatar" />
      <span className="user-name">{user.name}</span>
    </div>
  );
}


// ===== ORGANISMS =====

// Organism: Site Header
function SiteHeader({ user, onSearch }) {
  return (
    <header className="site-header">
      {/* Logo molecule */}
      <div className="logo">
        <img src="/logo.svg" alt="MyApp" className="logo-image" />
        <span className="logo-text">MyApp</span>
      </div>
      
      {/* Navigation */}
      <nav className="main-nav">
        <a href="/home" className="nav-link">Home</a>
        <a href="/products" className="nav-link">Products</a>
        <a href="/about" className="nav-link">About</a>
      </nav>
      
      {/* Search molecule */}
      <SearchForm onSearch={onSearch} />
      
      {/* User molecule */}
      <UserBadge user={user} />
    </header>
  );
}

// Organism: Product Grid
function ProductGrid({ products }) {
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Organism: Product Card
function ProductCard({ product }) {
  return (
    <article className="product-card">
      <img
        src={product.image}
        alt={product.name}
        className="product-image"
      />
      
      <div className="product-info">
        <Heading level={3} size="md">{product.name}</Heading>
        <p className="product-description">{product.description}</p>
        
        <div className="product-price">
          <span className="price-label">Price:</span>
          <span className="price-value">${product.price}</span>
        </div>
        
        <Button variant="primary" onClick={() => addToCart(product)}>
          Add to Cart
        </Button>
      </div>
    </article>
  );
}


// ===== TEMPLATES =====

// Template: E-commerce Homepage Layout
function HomepageTemplate({ header, hero, featuredProducts, footer }) {
  return (
    <div className="homepage-template">
      {header}
      
      <main>
        <section className="hero-section">
          {hero}
        </section>
        
        <section className="featured-products-section">
          <Heading level={2} size="xl">Featured Products</Heading>
          {featuredProducts}
        </section>
      </main>
      
      {footer}
    </div>
  );
}

// Template: Dashboard Layout
function DashboardTemplate({ sidebar, mainContent, widgets }) {
  return (
    <div className="dashboard-template">
      <aside className="dashboard-sidebar">
        {sidebar}
      </aside>
      
      <main className="dashboard-main">
        <div className="dashboard-widgets">
          {widgets}
        </div>
        
        <div className="dashboard-content">
          {mainContent}
        </div>
      </main>
    </div>
  );
}


// ===== PAGES =====

// Page: Actual Homepage with real content
function Homepage() {
  const user = { name: 'Alice', avatar: '/alice.jpg' };
  const products = [
    { id: 1, name: 'Laptop', description: 'Powerful laptop', price: 999, image: '/laptop.jpg' },
    { id: 2, name: 'Mouse', description: 'Wireless mouse', price: 29, image: '/mouse.jpg' }
  ];
  
  return (
    <HomepageTemplate
      header={<SiteHeader user={user} onSearch={query => console.log(query)} />}
      hero={
        <div className="hero">
          <Heading level={1} size="2xl">Welcome to MyApp</Heading>
          <p>Find the best products</p>
          <Button variant="primary" size="lg">Shop Now</Button>
        </div>
      }
      featuredProducts={<ProductGrid products={products} />}
      footer={<footer className="site-footer">© 2024 MyApp</footer>}
    />
  );
}

Advanced Example: Atomic Design in Vue with Composition API

<!-- ===== ATOMS ===== -->

<!-- Button.vue -->
<template>
  <button
    :class="['btn', `btn-${variant}`, `btn-${size}`]"
    :type="type"
    @click="$emit('click', $event)">
    <slot />
  </button>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

defineProps({
  variant: { type: String, default: 'primary' },
  size: { type: String, default: 'md' },
  type: { type: String, default: 'button' }
});

defineEmits(['click']);
</script>


<!-- Input.vue -->
<template>
  <input
    :type="type"
    :value="modelValue"
    :placeholder="placeholder"
    :aria-invalid="invalid"
    class="input"
    @input="$emit('update:modelValue', $event.target.value)">
</template>

<script setup>
defineProps({
  type: { type: String, default: 'text' },
  modelValue: String,
  placeholder: String,
  invalid: Boolean
});

defineEmits(['update:modelValue']);
</script>


<!-- ===== MOLECULES ===== -->

<!-- FormField.vue -->
<template>
  <div class="form-field">
    <label :for="id" class="label">{{ label }}</label>
    <Input
      :id="id"
      v-model="inputValue"
      :type="type"
      :placeholder="placeholder"
      :invalid="!!error"
    />
    <span v-if="error" class="error-message" role="alert">
      {{ error }}
    </span>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import Input from './Input.vue';

const props = defineProps({
  label: String,
  id: String,
  type: String,
  placeholder: String,
  modelValue: String,
  error: String
});

const emit = defineEmits(['update:modelValue']);

const inputValue = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
});
</script>


<!-- SearchForm.vue -->
<template>
  <form class="search-form" @submit.prevent="handleSearch">
    <Input
      v-model="query"
      type="search"
      :placeholder="placeholder"
    />
    <Button type="submit" variant="primary">
      Search
    </Button>
  </form>
</template>

<script setup>
import { ref } from 'vue';
import Input from './Input.vue';
import Button from './Button.vue';

defineProps({
  placeholder: { type: String, default: 'Search...' }
});

const emit = defineEmits(['search']);

const query = ref('');

function handleSearch() {
  emit('search', query.value);
}
</script>


<!-- ===== ORGANISMS ===== -->

<!-- ProductCard.vue -->
<template>
  <article class="product-card">
    <img
      :src="product.image"
      :alt="product.name"
      class="product-image">
    
    <div class="product-info">
      <h3 class="heading heading-md">{{ product.name }}</h3>
      <p class="product-description">{{ product.description }}</p>
      
      <div class="product-price">
        <span class="price-label">Price:</span>
        <span class="price-value">${{ product.price }}</span>
      </div>
      
      <Button variant="primary" @click="$emit('add-to-cart', product)">
        Add to Cart
      </Button>
    </div>
  </article>
</template>

<script setup>
import Button from './Button.vue';

defineProps({
  product: {
    type: Object,
    required: true
  }
});

defineEmits(['add-to-cart']);
</script>


<!-- SiteHeader.vue -->
<template>
  <header class="site-header">
    <div class="logo">
      <img src="/logo.svg" alt="MyApp" class="logo-image">
      <span class="logo-text">MyApp</span>
    </div>
    
    <nav class="main-nav">
      <a href="/home" class="nav-link">Home</a>
      <a href="/products" class="nav-link">Products</a>
      <a href="/about" class="nav-link">About</a>
    </nav>
    
    <SearchForm @search="handleSearch" />
    
    <div class="user-badge">
      <img :src="user.avatar" alt="" class="avatar">
      <span class="user-name">{{ user.name }}</span>
    </div>
  </header>
</template>

<script setup>
import SearchForm from './SearchForm.vue';

defineProps({
  user: {
    type: Object,
    required: true
  }
});

const emit = defineEmits(['search']);

function handleSearch(query) {
  emit('search', query);
}
</script>


<!-- ===== TEMPLATES ===== -->

<!-- HomepageTemplate.vue -->
<template>
  <div class="homepage-template">
    <slot name="header" />
    
    <main>
      <section class="hero-section">
        <slot name="hero" />
      </section>
      
      <section class="featured-products-section">
        <h2 class="heading heading-xl">Featured Products</h2>
        <slot name="featured-products" />
      </section>
    </main>
    
    <slot name="footer" />
  </div>
</template>


<!-- ===== PAGES ===== -->

<!-- HomePage.vue -->
<template>
  <HomepageTemplate>
    <template #header>
      <SiteHeader :user="user" @search="handleSearch" />
    </template>
    
    <template #hero>
      <div class="hero">
        <h1 class="heading heading-2xl">Welcome to MyApp</h1>
        <p>Find the best products</p>
        <Button variant="primary" size="lg">Shop Now</Button>
      </div>
    </template>
    
    <template #featured-products>
      <div class="product-grid">
        <ProductCard
          v-for="product in products"
          :key="product.id"
          :product="product"
          @add-to-cart="addToCart"
        />
      </div>
    </template>
    
    <template #footer>
      <footer class="site-footer">© 2024 MyApp</footer>
    </template>
  </HomepageTemplate>
</template>

<script setup>
import { ref } from 'vue';
import HomepageTemplate from './HomepageTemplate.vue';
import SiteHeader from './SiteHeader.vue';
import ProductCard from './ProductCard.vue';
import Button from './Button.vue';

const user = ref({ name: 'Alice', avatar: '/alice.jpg' });

const products = ref([
  { id: 1, name: 'Laptop', description: 'Powerful laptop', price: 999, image: '/laptop.jpg' },
  { id: 2, name: 'Mouse', description: 'Wireless mouse', price: 29, image: '/mouse.jpg' }
]);

function handleSearch(query) {
  console.log('Search:', query);
}

function addToCart(product) {
  console.log('Add to cart:', product);
}
</script>

Common Mistakes

1. Skipping Atoms and Creating One-Off Components

Mistake: Building molecules directly without reusable atoms.

// ❌ BAD: One-off search form with no reusable atoms
function SearchForm() {
  return (
    <form>
      {/* Hardcoded styles, no reusability */}
      <input
        type="search"
        style={{ padding: '8px', border: '1px solid #ccc' }}
        placeholder="Search..."
      />
      <button style={{ background: '#0066cc', color: 'white', padding: '8px 16px' }}>
        Search
      </button>
    </form>
  );
}

// ✅ GOOD: Build from reusable atoms
function Button({ children, ...props }) {
  return <button className="btn btn-primary" {...props}>{children}</button>;
}

function Input({ type = 'text', ...props }) {
  return <input className="input" type={type} {...props} />;
}

function SearchForm() {
  return (
    <form className="search-form">
      <Input type="search" placeholder="Search..." />
      <Button type="submit">Search</Button>
    </form>
  );
}
// Now Button and Input can be reused elsewhere

Why it matters: Without atoms, you’ll duplicate code and have inconsistent styling across your app.

2. Confusing Molecules with Organisms

Mistake: Creating overly complex molecules or too-simple organisms.

// ❌ BAD: Molecule doing too much (this is an organism)
function UserProfileMolecule({ user }) {
  return (
    <div className="user-profile">
      {/* Too complex for a molecule */}
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <ul>
        {user.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
      <button>Follow</button>
      <button>Message</button>
    </div>
  );
}

// ✅ GOOD: Break into proper hierarchy

// Molecule: User Badge (simple, single purpose)
function UserBadge({ user }) {
  return (
    <div className="user-badge">
      <img src={user.avatar} alt="" className="avatar" />
      <span>{user.name}</span>
    </div>
  );
}

// Molecule: Post List Item
function PostListItem({ post }) {
  return (
    <li className="post-item">
      <a href={`/posts/${post.id}`}>{post.title}</a>
    </li>
  );
}

// Organism: User Profile Card (combines molecules)
function UserProfileCard({ user }) {
  return (
    <div className="user-profile-card">
      <UserBadge user={user} />
      <p className="user-bio">{user.bio}</p>
      
      <ul className="post-list">
        {user.posts.map(post => (
          <PostListItem key={post.id} post={post} />
        ))}
      </ul>
      
      <div className="actions">
        <Button>Follow</Button>
        <Button variant="secondary">Message</Button>
      </div>
    </div>
  );
}

Why it matters: Molecules should be simple and focused. Complex components should be organisms.

3. Creating Page-Specific Components Instead of Reusable Organisms

Mistake: Building components that only work on one page.

// ❌ BAD: HomePage-specific component
function HomePageProductGrid({ products }) {
  return (
    <div className="homepage-product-grid">
      <h2>Featured on Homepage</h2>  {/* Hardcoded text */}
      <div className="grid-4-col">  {/* Hardcoded 4 columns */}
        {products.slice(0, 4).map(product => (  /* Hardcoded limit */
          <div className="product-card">
            <img src={product.image} alt={product.name} />
            <h3>{product.name}</h3>
            <span>${product.price}</span>
          </div>
        ))}
      </div>
    </div>
  );
}
// Can only be used on homepage!

// ✅ GOOD: Reusable organism with props
function ProductGrid({ products, columns = 3, heading }) {
  return (
    <div className="product-grid">
      {heading && <h2 className="grid-heading">{heading}</h2>}
      <div className={`grid-${columns}-col`}>
        {products.map(product => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
    </div>
  );
}

// Now works on any page
function HomePage() {
  return <ProductGrid products={featured} columns={4} heading="Featured Products" />;
}

function CategoryPage() {
  return <ProductGrid products={allProducts} columns={3} heading="All Products" />;
}

Why it matters: Reusable organisms reduce code duplication and ensure consistency across pages.

Quick Quiz

What are the five levels of Atomic Design, in order from smallest to largest?

What is the key difference between a Template and a Page?

Which of these is the most accurate definition of a molecule?

When does it make sense to promote a molecule to an organism?

Why is a shared atomic-design vocabulary useful across a team?

References