DEV Community

Cover image for The Container & Presentational Pattern: Separation of Concerns in React
Masudur Rahaman Sourav
Masudur Rahaman Sourav

Posted on

The Container & Presentational Pattern: Separation of Concerns in React

If you’ve been writing React for a while, you’ve probably created a "God Component." You know the one. It fetches data, manages fifty state variables, handles form validation, calculates the Fibonacci sequence for some reason, and also renders the HTML. It’s a messy, co-dependent relationship where everyone is screaming.

Enter the Container/Presentational Pattern (also known as Smart vs. Dumb components, though we try to be nicer about the naming these days). It’s couples therapy for your code.

🎨 The Graphical Explanation

Imagine a high-end restaurant.

The Container ( The Chef / Manager ): Sweating in the back. Dealing with suppliers (APIs), managing inventory (State), and screaming about hygiene standards (Business Logic). They don't care what the plate looks like; they just care that the food exists.

The Presentational ( The Waiter / Plater ): Takes the prepared food, arranges it beautifully, and smiles at the customer. They don't know how the sausage is made, and frankly, they don't want to know.

Here is how it looks in your app structure:

   +-------------------------+
   |   CONTAINER COMPONENT   |
   |  (The Brains / Logic)   |
   +-------------------------+
   | 1. Fetches Data (API)   |
   | 2. Manages State        |
   | 3. Defines Handlers     |
   +-----------+-------------+
               |
    (Passes Data via Props)
               |
               v
   +-------------------------+
   | PRESENTATIONAL COMPONENT|
   |  (The Looks / UI)       |
   +-------------------------+
   | 1. Receives props       |
   | 2. Renders JSX / CSS    |
   | 3. Looks Fabulous       |
   +-------------------------+
Enter fullscreen mode Exit fullscreen mode

💻 The Code: A Tale of Two Components

Let's look at a classic example: A list of Crypto Currencies.

1. The Presentational Component (The "Pretty" One)

This component is pure. It has no idea where the data comes from. It just takes props and turns them into pixels. It has zero anxiety.


// CryptoList.jsx
import React from 'react';

const CryptoList = ({ coins, isLoading, onRefresh }) => {
  if (isLoading) {
    return <div className="spinner">Loading your digital gold...</div>;
  }

  return (
    <div className="crypto-card">
      <h2>🚀 To The Moon!</h2>
      <ul>
        {coins.map((coin) => (
          <li key={coin.id} className="coin-row">
            <strong>{coin.name}</strong>: ${coin.price.toFixed(2)}
          </li>
        ))}
      </ul>
      <button onClick={onRefresh}>Refresh Prices</button>
    </div>
  );
};

export default CryptoList;

Enter fullscreen mode Exit fullscreen mode

2. The Container Component (The "Smart" One)

This component does the heavy lifting. It doesn't care about CSS classes. It cares about useEffect, fetch, and handling errors without crying.

// CryptoListContainer.jsx
import React, { useState, useEffect } from 'react';
import CryptoList from './CryptoList';

const CryptoListContainer = () => {
  const [coins, setCoins] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchCoinData = async () => {
    setLoading(true);
    // Simulating an API call because I don't have an API key handy
    setTimeout(() => {
      setCoins([
        { id: 1, name: 'Bitcoin', price: 45000 },
        { id: 2, name: 'Ethereum', price: 3200 },
        { id: 3, name: 'Doge', price: 0.12 },
      ]);
      setLoading(false);
    }, 1000);
  };

  useEffect(() => {
    fetchCoinData();
  }, []);

  // Notice: No HTML here, just passing data down!
  return (
    <CryptoList 
      coins={coins} 
      isLoading={loading} 
      onRefresh={fetchCoinData} 
    />
  );
};

export default CryptoListContainer;
Enter fullscreen mode Exit fullscreen mode

🏆 Why Should You Do This? (Use Cases)

1. Reusability (The Outfit Change)

You can use the same CryptoList UI to display data from a different source (like a "Favorites" list vs "All Coins" list) without rewriting the styling logic.

2. Separation of Concerns (Mental Health)

When you need to fix a styling bug (margin issues, ugh), you open the Presentational file. When the data isn't loading, you open the Container. You don't have to scroll past 400 lines of logic to find the

tag.

3. Designer/Developer Harmony

If you have a designer on your team who knows HTML/CSS but is scared of useEffect, they can work safely in the Presentational files without accidentally creating an infinite render loop that crashes the browser.

🕳️ The Pitfalls (The "Gotchas")

1. The "Hook" Twist

Plot twist! Since React Hooks (useCustomHook) came out in 2019, strictly creating "Container Components" (class-style) is less common.
Nowadays, the Container is often replaced by a Custom Hook.

  • Old Way:
    Container Component wraps UI Component.

  • New Way:
    UI Component calls useCryptoData() hook.

  • Verdict:
    The concept (separation of logic/view) is still king, but the implementation has shifted.

2. Over-Engineering

Don't create a Container/Presenter pair for a simple "Submit Button." If a component is 10 lines of code, splitting it into two files just makes you look pretentious. Keep it simple.

3. Prop Drilling Hell

If you have containers inside containers inside containers, passing props down becomes a nightmare. If you find yourself passing user={user} down 15 levels, stop. Get help. Use Context.

🏁 Conclusion

The Container/Presentational pattern is like separating your laundry into "Whites" and "Colors." It takes a little extra effort upfront, but it prevents your codebase from turning into a muddy, grey mess later on.

Keep your UI dumb and your logic smart, and your React app will be a happy place. 🚀

Top comments (0)