Explain the presentational vs container component pattern
The short answer
The presentational vs container pattern is a way of splitting your components into two categories: presentational components that focus on how things look, and container components that focus on how things work. Presentational components receive data through props and render UI. Container components manage state, handle logic, and pass data down. The idea is to separate concerns so each component has a single, clear job.
Presentational components
A presentational component is all about rendering. It takes in props and returns JSX. It doesn't fetch data, manage state, or contain business logic. It's a pure function of its inputs.
function UserCard({ name, email, avatarUrl }) {
return (
<div className="user-card">
<img src={avatarUrl} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
</div>
);
}This component doesn't know where the user data comes from. It could be from an API, from local state, from a Redux store — it doesn't care. You hand it a name, email, and avatar URL, and it renders a card. That's it.
This makes the component incredibly reusable. You can drop it into any context, any page, any project. You can render it in Storybook with hardcoded props. You can test it by checking that the right text appears for the right inputs. No mocking, no setup, no fuss.
Container components
A container component handles the messy parts — data fetching, state management, side effects, and business logic. It then passes the results down to presentational components.
function UserCardContainer({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <Spinner />;
return (
<UserCard
name={user.name}
email={user.email}
avatarUrl={user.avatar}
/>
);
}The container does all the work — fetches the user, manages loading state — and then hands clean data to UserCard. The presentational component stays simple and focused.
Why this pattern emerged
This pattern was popularized by Dan Abramov (one of the React core team members) around 2015, and it solved real problems:
- Separation of concerns — your UI code isn't tangled with your data logic. You can change how data is fetched without touching the UI, and vice versa.
- Reusability — presentational components are trivially reusable because they have no dependencies on specific data sources.
- Testability — testing a presentational component is just "given these props, does it render correctly?" No API mocking needed.
- Team collaboration — designers and UI-focused developers can work on presentational components while backend-focused developers handle containers.
Why this pattern has fallen out of favor
Here's where things get interesting. Dan Abramov himself wrote an update to his original blog post saying he no longer recommends this split. The reason? Custom hooks.
Before hooks, there was no clean way to extract stateful logic from a component without using a pattern like containers, higher-order components, or render props. Containers were the cleanest option at the time.
But with hooks, you can extract logic into a custom hook and use it directly in the component that needs it:
function useUser(userId) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => {
setUser(data);
setLoading(false);
});
}, [userId]);
return { user, loading };
}
function UserCard({ userId }) {
const { user, loading } = useUser(userId);
if (loading) return <Spinner />;
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}The logic lives in useUser. The component uses the hook directly. No need for a separate container. The hook itself is reusable and testable, and the component stays straightforward.
When it's still useful
Even though hooks have replaced most use cases for containers, the spirit of the pattern — separating "how it looks" from "how it works" — is still valuable:
- Design system components — if you're building a component library, your buttons, cards, and inputs should be purely presentational. They should accept data through props and render UI. That's the contract.
- Storybook-friendly components — presentational components are perfect for visual testing and documentation because you can render them with any props.
- Complex pages — when a page has a lot of data orchestration, it can still help to have a top-level component that coordinates data and passes it down to simpler rendering components.
The takeaway isn't "never use this pattern." It's "don't rigidly enforce it as a rule." Think of it as a useful mental model — separate your concerns — but use hooks as the primary mechanism for extracting logic.
Why interviewers ask this
This question tests whether you understand React's evolution. Knowing the pattern shows you've thought about component architecture. Knowing why it's less common now shows you understand hooks and how they changed React's mental model. The best answers demonstrate that you can make architectural decisions based on the tools available, not just follow patterns blindly.