Container vs Presentational Components
Container vs Presentational Components
The Container vs. Presentational component pattern (sometimes called Smart vs. Dumb components) is a classic React architectural pattern popularized by Dan Abramov around 2015.
While its strict necessity has faded with the introduction of React Hooks, understanding this pattern is crucial for maintaining a clean separation of concerns in large applications.
The Core Idea
The pattern suggests splitting components into two distinct categories based on their primary responsibility:
- Presentational Components (Dumb): Concerned only with how things look.
- Container Components (Smart): Concerned only with how things work.
Presentational Components
These components are responsible for the UI. They are usually stateless and receive all their data and callbacks exclusively via props.
Characteristics:
- They render HTML or other Presentational components.
- They have no dependencies on the rest of the app (like Redux stores, API calls, or global contexts).
- They rarely have their own state (except for UI state, like
isHovered). - They are highly reusable.
// Presentational Component: Only cares about UI
function UserCard({ name, avatarUrl, onFollow }) {
return (
<div className="card">
<img src={avatarUrl} alt={name} />
<h2>{name}</h2>
<button onClick={onFollow}>Follow</button>
</div>
);
}
Container Components
These components are responsible for logic and data fetching. They do little to no rendering of actual HTML elements. Instead, they pass data down to Presentational components.
Characteristics:
- They render Presentational components.
- They fetch data, interact with Redux/Context, and manage complex state.
- They are often generated by Higher-Order Components (like Redux’s
connect()). - They are usually not highly reusable (because they are tied to specific data sources).
import { useState, useEffect } from 'react';
// Container Component: Only cares about data and logic
function UserCardContainer({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
const handleFollow = () => {
fetch(`/api/users/${userId}/follow`, { method: 'POST' });
};
if (!user) return <LoadingSpinner />;
// Render the Presentational component, passing the data as props
return (
<UserCard
name={user.name}
avatarUrl={user.avatarUrl}
onFollow={handleFollow}
/>
);
}
Why use this pattern?
- Separation of Concerns: Designers can tweak the CSS in
UserCardwithout accidentally breaking the API logic inUserCardContainer. - Reusability: You can reuse
UserCardwith completely different data sources (e.g., using it for fake mock data in a Storybook or testing environment). - Readability: It makes it easier to understand your codebase. If a component has complex UI, you look in the Presentational file. If data isn’t loading, you look in the Container file.
Is this pattern still relevant with Hooks?
Yes, but the implementation has changed.
In 2019, Dan Abramov updated his original article stating that the introduction of Custom Hooks makes the strict Container/Presentational split less necessary.
Instead of wrapping a Presentational component in a Container component, you can now extract the logic into a Custom Hook, and use it directly inside the UI component.
// Modern Approach: Logic extracted to a Hook
function useUser(userId) {
// ... state and fetch logic
return { user, followUser };
}
// The UI component uses the Hook directly
function ModernUserCard({ userId }) {
const { user, followUser } = useUser(userId);
if (!user) return <LoadingSpinner />;
return (
<div className="card">
<img src={user.avatarUrl} alt={user.name} />
<h2>{user.name}</h2>
<button onClick={followUser}>Follow</button>
</div>
);
}
However, the philosophy remains the same: Separate your logic (Hooks/Containers) from your UI (JSX/Presentational).
Interview Questions
Q: What is the difference between a Container and Presentational component?
A: A Presentational component is purely visual, stateless, and relies entirely on props (e.g., rendering a button or a styled list). A Container component manages state, data fetching, and business logic, and passes that data down to the Presentational component. (Note: While still valid, React Hooks have largely made this strict physical separation obsolete by allowing stateful logic to be extracted into custom hooks instead).