Frontend Architecture Container Presentational
When to use this skill
- Building component libraries
- Need highly testable UI components
- Separating business logic from presentation
- Creating reusable design systems
Workflow
- Identify which components handle logic vs display
- Create presentational components (pure UI)
- Create container components (logic + state)
- Connect containers to data sources
- Test presentational components in isolation
Instructions
Component Types
Presentational Components
- Focus on how things look
- Receive data via props
- No state management (usually)
- Highly reusable
Container Components
- Focus on how things work
- Manage state, side effects
- Connect to APIs, stores
- Render presentational components
Example: User Profile
Presentational Component
// components/UserProfile.tsx
interface UserProfileProps {
name: string;
email: string;
avatarUrl: string;
onEdit: () => void;
isLoading?: boolean;
}
export function UserProfile({
name,
email,
avatarUrl,
onEdit,
isLoading = false
}: UserProfileProps) {
if (isLoading) {
return <Skeleton />;
}
return (
<div className="profile">
<img src={avatarUrl} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
<button Profile</button>
</div>
);
}
Container Component
// containers/UserProfileContainer.tsx
import { UserProfile } from '../components/UserProfile';
export function UserProfileContainer({ userId }: { userId: string }) {
const { data, isLoading } = useQuery(['user', userId], () =>
fetchUser(userId)
);
const navigate = useNavigate();
const handleEdit = () => {
navigate(`/users/${userId}/edit`);
};
return (
<UserProfile
name={data?.name ?? ''}
email={data?.email ?? ''}
avatarUrl={data?.avatarUrl ?? ''}
isLoading={isLoading}
/>
);
}
Folder Structure
src/
├── components/ # Presentational
│ ├── Button.tsx
│ ├── Card.tsx
│ └── UserProfile.tsx
├── containers/ # Container
│ ├── UserProfileContainer.tsx
│ └── ProductListContainer.tsx
└── features/
└── users/
├── components/ # Feature-specific presentational
└── containers/ # Feature-specific containers
Modern Alternative: Hooks
With hooks, you can extract logic without container components:
// hooks/useUserProfile.ts
export function useUserProfile(userId: string) {
const { data, isLoading } = useQuery(['user', userId], () =>
fetchUser(userId)
);
const navigate = useNavigate();
const handleEdit = () => {
navigate(`/users/${userId}/edit`);
};
return {
user: data,
isLoading,
handleEdit
};
}
// components/UserProfile.tsx (Smart component with hook)
export function UserProfile({ userId }: { userId: string }) {
const { user, isLoading, handleEdit } = useUserProfile(userId);
if (isLoading) return <Skeleton />;
if (!user) return null;
return (
<div className="profile">
<img src={user.avatarUrl} alt={user.name} />
<h2>{user.name}</h2>
<button
</div>
);
}
When to Use Each
Presentational Components:
- Design system components
- Storybook documentation
- High reusability needed
Container Components:
- Legacy codebases
- Class components
- Complex orchestration
Custom Hooks (Modern):
- New React codebases
- Functional components
- Better composition
Resources
- Presentational components are easy to test
- Containers handle complexity
- Hooks are the modern approach