Back to all notes

Smart vs Dumb Components: A Practical Hierarchy for Features and UI

Keep feature logic in smart components and render with simple, reusable UI components.

2 min read

Introduction

“Smart” components manage data and interactions; “dumb” components only render UI. This hierarchy lets you build complex features from simple visual building blocks.

Why this matters

Smart (feature) components handle data and interactions. Dumb (UI) components render the interface. This separation makes features composable and UI reusable.

The problem

Keeping state and markup together at the page level blurs concerns. There’s no clean split between behavior and UI, so reuse is limited.

Inefficient approach

Page-level components render feature UI directly:

import { useState } from "react";

function App() {
  const [article, setArticle] = useState({
    title: "Understanding React Patterns",
    content: "React patterns help us write better, more maintainable code...",
    likes: 0,
  });

  const handleLike = () => {
    setArticle((prev) => ({ ...prev, likes: prev.likes + 1 }));
  };

  return (
    <div>
      <h1>Smart vs Dumb Components</h1>
      <h2>Understanding React Patterns</h2>
      <p>React patterns help us write better, more maintainable code...</p>
      <button onClick={handleLike}>Like ({article.likes})</button>
    </div>
  );
}

export default App;

The solution

Create small, focused UI components and a smart feature component that orchestrates them.

import { useState } from "react";

function Card({ title, content }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <p>{content}</p>
    </div>
  );
}

function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

function ArticleFeature() {
  const [article, setArticle] = useState({
    title: "Understanding React Patterns",
    content: "React patterns help us write better, more maintainable code...",
    likes: 0,
  });

  const handleLike = () => {
    setArticle((prev) => ({ ...prev, likes: prev.likes + 1 }));
  };

  return (
    <div>
      <Card title={article.title} content={article.content} />
      <Button onClick={handleLike}>Like ({article.likes})</Button>
    </div>
  );
}

function App() {
  return (
    <div>
      <h1>Smart vs Dumb Components</h1>
      <ArticleFeature />
    </div>
  );
}

export default App;

Step-by-step

  1. Create dumb UI components: Card and Button.
  2. Move feature state (article) and handlers into ArticleFeature.
  3. Compose UI: pass only the data UI needs (title, content).
  4. Wire actions: pass handleLike to Button.
  5. Keep the page responsible only for placing the feature.

Why this is better

  • UI components stay stateless and reusable.
  • Feature components stay focused on behavior and orchestration.
  • Pages compose features, which compose UI. Clean layers.

Tips

  • Keep UI components pure and style‑focused; avoid side effects.
  • If a UI component starts handling state, ask whether it’s becoming a feature.
  • Extract repeated UI patterns early—it pays off quickly in larger apps.

Knowledge base

Problem snippet:

<h2>Understanding React Patterns</h2>
<button onClick={handleLike}>Like ({article.likes})</button>

Solution snippet:

<Card title={article.title} content={article.content} />
<Button onClick={handleLike}>Like ({article.likes})</Button>