Component Design Checklist
When to use: Building new UI components. Reviewing component architecture.
The test: Can this component be used in isolation? Can it be understood in under 30 seconds?
Component Creation Checklist
Run this for every new component:
- Named component - No anonymous exports
- TypeScript interface - Props defined with types
- Semantic HTML - article, section, button - not div soup
- Design tokens - No arbitrary values (use theme)
- Destructured props - With default values
- Single responsibility - Does one thing well
- Independently testable - Works in isolation
Component Types
| Type | Purpose | Data Source | Location |
|---|---|---|---|
| Presentational | Display UI | Props only | Reusable library |
| Structural | Compose layout | Props, maps data | Pages/views |
| Stateful | Manage state | API, context | App-specific |
Rule of Thumb
- Bottom of tree → Presentational (most reusable)
- Middle of tree → Structural (compose others)
- Top of tree → Stateful (app-specific)
Props Design
Checklist
- Props destructured at function signature
- Defaults assigned during destructuring
- No more than 5-7 props (split if more)
- No boolean props for complex states
- Children over render props where possible
Good Example
interface CardProps {
title: string;
description: string;
variant?: 'default' | 'highlighted';
onAction?: () => void;
}
function Card({
title,
description,
variant = 'default',
onAction
}: CardProps) {
// ...
}
Bad Component Signals
| Signal | Problem | Fix |
|---|---|---|
| Too many props | Too many responsibilities | Split into smaller components |
| Breaks when moved | Too coupled | Accept data via props |
| Hard to test | Hidden dependencies | Inject dependencies |
| Render props hell | Over-abstraction | Use children or composition |
| Prop drilling | Wrong tree structure | Use context or restructure |
File Organization
src/components/
├── design-system/ # Presentational (most reusable)
│ ├── Button.tsx
│ ├── Typography.tsx
│ └── Card.tsx
├── features/ # Structural (feature-specific)
│ ├── auth/
│ └── dashboard/
└── pages/ # Stateful (app-specific)
└── HomePage.tsx
Conditional Rendering
Checklist
- No nested ternaries
- Early returns for error/loading states
- Logical && only for simple cases
- Named variables for complex conditions
Good Example
function UserProfile({ user, isLoading, error }) {
if (isLoading) return <Skeleton />;
if (error) return <ErrorState error={error} />;
if (!user) return null;
return <Profile user={user} />;
}
Avoid These Anti-Patterns
- No nested render functions - Extract to components
- No inline object props - Creates new reference each render
- No index as key - Use stable IDs
- No side effects in render - Use useEffect
- No direct DOM manipulation - Use refs
Testing Checklist
- Component renders without crashing
- Props produce expected output
- Events trigger correct callbacks
- Accessibility attributes present
- Works in Storybook isolation
References
- Component library:
src/components/design-system/ - Design tokens:
tailwind.config.js - Component Driven
- Storybook Testing