Dumb Components (aka Presentational Components)
Focus only on UI: Mostly JSX
Stateless: Don’t handle business logic or state (except maybe local UI state)
Receive data and callbacks via props
//Button.tsx
function Button({ label, onClick }) {
return <button onClick={onClick}>{label}</button>;
}
//UserCard.tsx
function UserCard({ name, age }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
</div>
);
}
These components are “dumb” because they don’t manage data; they just render props.
Smart Components (aka Container Components)
Focus on how things work (logic, data fetching, state management)
Pass data down to dumb components
Often stateful
Can handle API calls, events, context, etc.
//UserList.tsx
import { useState, useEffect } from "react";
import UserCard from "./UserCard"; // dumb component
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("/api/users")
.then(res => res.json())
.then(data => setUsers(data));
}, []);
return (
<div>
{users.map(user => (
<UserCard key={user.id} name={user.name} age={user.age} />
))}
</div>
);
}
UserList
is smart, it fetches data and manages state, but delegates UI to dumb components.
Why even use this pattern?
Separates UI from logic, easier to maintain
Makes components reusable
Easier to test UI without worrying about logic
Follows the Single Responsibility Principle
Note. Modern React with hooks sometimes blurs the line, because even “dumb” components can use hooks for local state. The idea is more about responsibility and separation of concerns than strictly whether a component has state.