Lesson 21 — Container vs Presentational Components (Separation of Concerns)

🧭 Introduction

As React applications grow, components start doing too much:

  • Fetching data
  • Managing state
  • Handling logic
  • Rendering UI

This leads to:
❌ Large components
❌ Poor readability
❌ Difficult testing
❌ Low reusability

To solve this, experienced React developers follow a classic and powerful design pattern:

Container vs Presentational Components

This lesson teaches you how to separate logic from UI, making your React code clean, scalable, and maintainable.


🎯 What You’ll Learn in This Lesson

By the end of this lesson, you will understand:

  • What container components are
  • What presentational components are
  • Why separation of concerns matters
  • How to structure components correctly
  • Real-world examples
  • When (and when not) to use this pattern

🧠 What Is Separation of Concerns?

Separation of concerns means:

Each part of your code should handle one responsibility only.

In React:

  • Business logic ≠ UI rendering
  • Data fetching ≠ styling

Mixing everything makes components hard to maintain.


🧩 Presentational Components (UI Components)

✅ What They Do

Presentational components:

  • Focus only on UI
  • Receive data via props
  • Do NOT manage state (usually)
  • Do NOT fetch data

They are also called:

  • UI components
  • Dumb components (older term)

🧠 Characteristics

✔ Stateless or minimal state
✔ Highly reusable
✔ Easy to test
✔ No business logic


🔍 Example — Presentational Component

function UserList({ users, onSelect }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id} onClick={() => onSelect(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}

Notice:

  • No API calls
  • No state management
  • Pure UI

🧩 Container Components (Logic Components)

✅ What They Do

Container components:

  • Manage state
  • Fetch data
  • Handle business logic
  • Pass data to presentational components

They are also called:

  • Smart components

🧠 Characteristics

✔ Stateful
✔ Logic-heavy
✔ Less reusable
✔ Coordinates app behavior


🔍 Example — Container Component

function UserListContainer() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);

  const handleSelect = user => {
    console.log(user);
  };

  return <UserList users={users} onSelect={handleSelect} />;
}

This component:

  • Fetches data
  • Manages state
  • Passes props to UI

🧠 How They Work Together

Container Component
        ↓
Presentational Component

The container:

  • Knows how things work

The presentational component:

  • Knows how things look

🆚 Before vs After (Why This Matters)

❌ Bad Practice (Everything Mixed)

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("/api/users").then(...);
  }, []);

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Problems:

  • Hard to reuse UI
  • Hard to test logic
  • Component grows quickly

✅ Good Practice (Separated)

<UserListContainer />

Internally:

  • Logic is isolated
  • UI is reusable
  • Code is clean

🧠 Benefits of This Pattern

✅ Better readability
✅ Reusable UI components
✅ Easier testing
✅ Cleaner architecture
✅ Easier refactoring

This pattern scales extremely well.


🏗️ Folder Structure Example

components/
  UserList.jsx        // Presentational
containers/
  UserListContainer.jsx

Clear responsibility boundaries.


🧠 Is This Pattern Still Relevant with Hooks?

Yes — absolutely.

Hooks:

  • Simplify containers
  • Do NOT eliminate separation of concerns

Hooks + this pattern = clean architecture.


🧠 Hooks + Container Pattern (Modern Approach)

Often combined with custom hooks:

function useUsers() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("/api/users").then(...);
  }, []);

  return users;
}

Then:

function UserListContainer() {
  const users = useUsers();
  return <UserList users={users} />;
}

✔ Clean
✔ Testable
✔ Reusable


❌ When NOT to Use This Pattern

Avoid it when:

  • Component is very small
  • Logic is minimal
  • Over-abstraction adds complexity

Simple components don’t need extra layers.


🚨 Common Mistakes

❌ Putting logic in presentational components

Breaks separation.


❌ Making presentational components state-heavy

Reduces reusability.


❌ Over-engineering small features

Balance is key.


🎯 Best Practices (Senior-Level)

✅ Separate UI and logic clearly
✅ Keep presentational components simple
✅ Use containers for orchestration
✅ Combine with custom hooks
✅ Avoid unnecessary abstraction


❓ FAQs — Container vs Presentational

🔹 Is this pattern mandatory?

No — but highly recommended for large apps.


🔹 Is this pattern outdated?

No — it’s timeless and still relevant.


🔹 Can I use Redux or React Query inside containers?

Yes — containers are ideal for that.


🔹 Is this asked in interviews?

Yes — especially for architecture discussions.


🧠 Quick Recap

✔ Containers handle logic
✔ Presentational components handle UI
✔ Separation improves maintainability
✔ Hooks enhance this pattern
✔ Don’t overuse it


🎉 Conclusion

Clean React code is not about more code
it’s about better separation.

Once you adopt the Container vs Presentational pattern:

  • Your components become simpler
  • Your UI becomes reusable
  • Your app scales gracefully

This lesson lays the foundation for professional React architecture ⚛️🏗️


👉 Next Lesson

Lesson 22 — Higher-Order Components (HOC Pattern)

Leave a Comment