Skip to content

Atomic Design Methodology

advanced15 min read

Why Component Organization Falls Apart at Scale

Every project starts clean. A components/ folder with 10 files. Then 30. Then 80. Then someone creates components/shared/. Then components/common/. Then components/ui/shared/common/. You now have three Button components and nobody knows which one to use.

Atomic design, created by Brad Frost, gives you a mental model for classifying components by their level of complexity. It is not a religion — it is a vocabulary that prevents the "shared/common/misc" chaos.

Mental Model

Think of components like chemistry. Atoms are elements on the periodic table — indivisible. Molecules are simple compounds — a few atoms bonded together. Organisms are complex structures — molecules working as a unit. Templates are the blueprint of a page. Pages are the final built thing with real data. You would never confuse an oxygen atom with a water molecule. Atomic design gives your components the same clarity.

The Five Levels

Atoms

The smallest, indivisible UI elements. They cannot be broken down further without ceasing to be useful.

// atoms/Button.tsx
interface ButtonProps {
  children: ReactNode;
  variant?: "primary" | "secondary" | "ghost";
  size?: "sm" | "md" | "lg";
  disabled?: boolean;
  onClick?: () => void;
}

function Button({ children, variant = "primary", size = "md", ...props }: ButtonProps) {
  return (
    <button className={buttonVariants({ variant, size })} {...props}>
      {children}
    </button>
  );
}

Other atoms: Input, Label, Badge, Avatar, Icon, Spinner, Separator.

Molecules

Small groups of atoms functioning as a unit. A molecule does one thing.

// molecules/SearchInput.tsx
function SearchInput({ value, onChange, placeholder }: SearchInputProps) {
  return (
    <div className="relative">
      <Icon name="search" className="absolute left-3 top-1/2 -translate-y-1/2" />
      <Input
        value={value}
        onChange={onChange}
        placeholder={placeholder}
        className="pl-10"
      />
    </div>
  );
}

A SearchInput is an Icon atom + an Input atom. Neither is useful alone in this context. Together, they form a recognizable, reusable unit.

Organisms

Complex, distinct sections of an interface. Organisms combine molecules and atoms into meaningful UI blocks.

// organisms/CourseCard.tsx
function CourseCard({ course }: { course: Course }) {
  return (
    <Card>
      <CardImage src={course.thumbnail} alt={course.title} />
      <CardContent>
        <Badge variant={course.difficulty}>{course.difficulty}</Badge>
        <Heading level={3}>{course.title}</Heading>
        <Text muted>{course.description}</Text>
        <ProgressBar value={course.progress} />
      </CardContent>
      <CardFooter>
        <Button variant="primary">Continue</Button>
      </CardFooter>
    </Card>
  );
}

Templates

Page-level layouts that define the structure without real data. Templates are the skeleton — they answer "where does everything go?"

// templates/CourseListTemplate.tsx
function CourseListTemplate({
  header,
  filters,
  courseGrid,
  pagination,
}: CourseListTemplateProps) {
  return (
    <div className="grid grid-cols-1 gap-8 lg:grid-cols-[280px_1fr]">
      <aside>{filters}</aside>
      <main>
        {header}
        {courseGrid}
        {pagination}
      </main>
    </div>
  );
}

Pages

Templates with real data. In Next.js App Router, these are your page.tsx files.

// app/courses/page.tsx
async function CoursesPage() {
  const courses = await getCourses();
  const categories = await getCategories();

  return (
    <CourseListTemplate
      header={<PageHeader title="All Courses" count={courses.length} />}
      filters={<CourseFilters categories={categories} />}
      courseGrid={<CourseGrid courses={courses} />}
      pagination={<Pagination total={courses.length} perPage={12} />}
    />
  );
}
Quiz
A SearchBar component contains an Input, a Button, and a dropdown of recent searches. What atomic level is it?

Mapping to File Structure

Here is a practical file structure that maps atomic design to a real Next.js project:

src/
  components/
    ui/              # Atoms + simple molecules (design system)
      button.tsx
      input.tsx
      badge.tsx
      search-input.tsx
      dialog.tsx
    blocks/           # Organisms (composed from ui/)
      course-card.tsx
      nav-header.tsx
      sidebar-nav.tsx
      quiz-block.tsx
    layouts/          # Templates
      course-list-layout.tsx
      lesson-layout.tsx
      dashboard-layout.tsx
  app/                # Pages (Next.js routes)
    courses/
      page.tsx
    dashboard/
      page.tsx

Notice: I did not name the folders atoms/, molecules/, organisms/. The atomic model is a thinking tool, not a folder-naming convention. ui/, blocks/, and layouts/ communicate intent better to developers who have not read Brad Frost's book.

Key Rules
  1. 1Use atomic thinking for classification, not for folder names — ui/, blocks/, layouts/ are clearer
  2. 2Atoms and molecules belong in ui/ — they are your design system primitives
  3. 3Organisms go in blocks/ — they are feature-specific compositions
  4. 4Templates go in layouts/ — they define page structure without data
  5. 5Pages are Next.js route files — they wire data to templates

When to Promote a Component

A common question: when does a molecule become an organism? When does an atom graduate to a molecule?

The promotion signals:

  • Atom to Molecule: When two atoms always appear together and have a combined purpose (Icon + Input = SearchInput)
  • Molecule to Organism: When the component needs its own state management, data fetching, or has more than 3-4 sub-components
  • Organism to Template: When the component defines spatial relationships between organisms without knowing what data fills them
Quiz
Your team has a components/shared/ folder with 47 files. What is the core problem?

The Problem with Shared and Common

Let us be direct: shared/, common/, utils/, and misc/ are where component architecture goes to die. They are non-categories. They tell you nothing about what is inside.

// THE GRAVEYARD
components/
  shared/
    Button.tsx          # atom
    DataTable.tsx       # organism
    useDebounce.ts      # hook (not a component)
    formatDate.ts       # utility (not a component)
    CourseCard.tsx       # organism
    Spinner.tsx         # atom

Every file has a different abstraction level. Finding anything requires opening every file.

What developers doWhat they should do
Putting everything reusable in components/shared/
Shared is a non-category. It will grow unbounded and developers will stop trusting the folder structure.
Classify by complexity level: ui/ for primitives, blocks/ for compositions, hooks/ for logic
Strict 1-to-1 mapping of atomic levels to folders (atoms/ molecules/ organisms/)
Most developers do not know atomic design terminology. Folder names should communicate intent without requiring prerequisite knowledge.
Use intuitive names (ui/ blocks/ layouts/) and use atomic thinking as a mental model
Making every small component an atom and every composition a molecule
Premature abstraction creates atoms nobody uses and molecules that serve one page. YAGNI applies to design systems too.
Only extract when reuse is actual, not hypothetical. Three instances minimum before abstracting.

Feature-Sliced Design: The Complement

Atomic design classifies by complexity. Feature-sliced design (FSD) classifies by domain. They complement each other beautifully.

src/
  components/
    ui/                   # Atomic: shared primitives (atoms + molecules)
      button.tsx
      input.tsx
      dialog.tsx
  features/
    course-progress/      # FSD: domain-specific organisms
      course-card.tsx
      progress-ring.tsx
      hooks.ts
    quiz/
      quiz-block.tsx
      quiz-timer.tsx
      hooks.ts
  layouts/                # Templates
    dashboard-layout.tsx
    lesson-layout.tsx

Shared primitives live in ui/. Domain-specific compositions live in their feature folder. This is how Vercel, Linear, and Stripe organize large codebases. The design system is atomic. The features are domain-sliced.

Quiz
Where should a ProgressRing component live if it is only used in the course-progress feature?

Real-World Design System Scale

Let us look at how this scales. A mature design system might have:

LevelCountExamplesChange Frequency
Atoms (ui/)15-25Button, Input, Badge, Avatar, IconRarely — these are stable primitives
Molecules (ui/)20-40SearchInput, FormField, NavLink, TooltipOccasionally — when atoms evolve
Organisms (blocks/)30-60CourseCard, NavHeader, QuizBlockFrequently — feature-driven
Templates (layouts/)5-10DashboardLayout, LessonLayoutRarely — structural changes are expensive
Pages (app/)20-50CoursesPage, DashboardPageConstantly — features ship here

Notice the pattern: the lower levels are stable, the higher levels change frequently. This is by design. Atoms are your foundation — you do not rebuild the foundation every sprint.

Quiz
Your design system has 80 atoms. What is likely wrong?
Interview Question

You are building a design system for a team of 30 engineers working on an app with 200+ components. How would you organize the component hierarchy? What rules would you set for when to create a new shared component vs keeping it feature-local? How would you handle component promotion (from feature-local to shared)?