Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Standardized guidelines for separating smart (container) components from dumb (presentational) components. Use when building reusable, testable React components with clear separation of logic and UI.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 5% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -7% | 0% |
| case-20 | ✓→✗ | ▼ Worse | 40% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 60% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 24% | 0% |
Presentational Components
Container Components
Presentational Component
typescript// 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 onClick={onEdit}>Edit Profile</button> </div> ); }
Container Component
typescript// 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 ?? ''} onEdit={handleEdit} isLoading={isLoading} /> ); }
src/
├── components/ # Presentational
│ ├── Button.tsx
│ ├── Card.tsx
│ └── UserProfile.tsx
├── containers/ # Container
│ ├── UserProfileContainer.tsx
│ └── ProductListContainer.tsx
└── features/
└── users/
├── components/ # Feature-specific presentational
└── containers/ # Feature-specific containersWith hooks, you can extract logic without container components:
typescript// 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 onClick={handleEdit}>Edit</button> </div> ); }
Presentational Components:
Container Components:
Custom Hooks (Modern):
Other measured skills in the registry, with their headline benchmark lift.