| name | react-component-design |
| description | When building or refactoring React UI components to ensure reusability and maintainability. |
| version | 2.0.0 |
| category | frontend |
| tags | ["frontend","react","ui","components"] |
| skill_type | workflow |
| author | skiLLM |
| license | MIT |
| compatible_agents | ["claude-code","cursor","copilot","codex"] |
| estimated_context_tokens | 1800 |
| dangerous | false |
| requires_review | false |
| security_level | safe |
| dependencies | [] |
| triggers | ["component","jsx","tsx","ui","reusable"] |
| permissions | {"filesystem":{"read":true,"write":true},"network":{"outbound":false},"shell":{"execute":false}} |
| input_requirements | ["existing React codebase","TypeScript project setup"] |
| output_contract | ["pure functional components","strict TypeScript interfaces","no prop drilling beyond 2 levels","CSS classes only"] |
| failure_conditions | ["component exceeds 200 lines","props interface not exported","side effects in render"] |
| last_updated | "2026-05-15T00:00:00.000Z" |
React Component Design
Purpose
React components MUST be pure, composable, and reusable abstractions. This skill enforces TypeScript-first design, separation of concerns, and deterministic rendering to ensure maintainable, testable UI code that works reliably across different contexts.
When to use
- Building a new UI element from scratch
- Refactoring a large, monolithic React component
- Extracting shared UI patterns into a component library
- Adding new features to an existing component hierarchy
- Preparing components for cross-team reuse
When NOT to use
- Styling implementation details (use DOM Security Hardening skill instead)
- State management architecture decisions (use State Management Patterns skill)
- Performance optimization via memo/useMemo (handle at call site, not in component)
Inputs required
- Existing React codebase with TypeScript
- Clear understanding of component's single responsibility
- Props interface definition (before implementation)
Workflow
- Define the API: Write the TypeScript interface for props BEFORE the component body. This clarifies the contract.
- Verify Props: Ensure props contain ONLY what the component needs—no unnecessary inherited types.
- Isolate Logic: Extract all complex state and side-effects into custom hooks (NEVER in component body).
- Render JSX: Build static JSX structure based solely on props and hook return values.
- Apply Styles: Use CSS classes via
className prop. NEVER use inline style={{...}}.
- Export Type: Export both the component AND its Props interface for consumers.
- Verify Size: Confirm file does NOT exceed 150 lines. If it does, extract sub-components.
Rules
- MUST be a pure function: identical props = identical output
- MUST export a strictly typed TypeScript interface named
[ComponentName]Props
- MUST NOT exceed 150 lines per file (extract sub-components if larger)
- MUST NOT use inline styles (
style={{...}})
- MUST NOT drill props more than 2 levels deep
- MUST NOT fetch data directly (use hooks or parent component)
- MUST support
className prop for customization
Anti-patterns
- Prop Drilling: Passing props down more than 2 levels deep (use Context, composition, or compound components)
- Inline Styles: Using
style={{...}} objects instead of CSS classes
- God Components: Handling data fetching, business logic, and UI rendering in one file
- State in Props: Copying prop values into local state with
useState(props.val)
- Implicit Dependencies: Accessing globals or services without prop parameters
- Class Components: Using class components instead of functional components with hooks
Failure conditions
- Component file exceeds 200 lines
- Props interface is not exported
- Component performs side effects without useEffect
- More than 2 levels of prop drilling detected
Validation checklist
Output format
- File count: 1 primary
.tsx file (+ additional files if refactored)
- Exports: One default export (component) + named export for Props interface
- Structure: Props interface → Component function → Export both
- Styling: CSS classes only, no inline styles
- Size: ≤ 150 lines per component file
Security considerations
- Components do NOT execute arbitrary user input; text content is always escaped
- Props are never used in DOM insertion without sanitization
- Event handlers are never created from strings or user input
- No use of
dangerouslySetInnerHTML (violates DOM Security Hardening)
Agent execution notes
- Agent MAY: Create new components, refactor large components, add TypeScript interfaces
- Agent MUST NEVER: Add inline styles, drill props beyond 2 levels, add data fetching to component
- Agent MUST ASK: Before exceeding 150 lines, before changing prop contracts
- Agent MUST VALIDATE: All TypeScript is strict, no prop drilling, component is pure functional
Example
❌ Anti-pattern (Prop drilling, inline styles, state mirroring):
export const Modal = ({ title, onClose, userId, userName, isOpen }) => {
const [name, setName] = useState(userName);
return (
<div style={{ position: 'fixed', top: 0 }} onClick={onClose}>
<h1>{title}</h1>
<input value={name} onChange={(e) => setName(e.target.value)} />
<UserProfile userId={userId} name={name} /> {/* 2 levels */}
</div>
);
};
export const UserProfile = ({ userId, name }) => {
return <UserBio userId={userId} name={name} />; {}
};
✅ Correct pattern (Pure, CSS classes, no drilling):
interface ModalProps {
title: string;
onClose: () => void;
isOpen: boolean;
children: React.ReactNode;
}
export const Modal = ({ title, onClose, isOpen, children }: ModalProps) => {
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content">
<h1>{title}</h1>
{children}
</div>
</div>
);
};
interface UserProfileProps {
userId: string;
}
export const UserProfile = ({ userId }: UserProfileProps) => {
const user = useUser(userId);
return (
<div>
<h2>{user?.name}</h2>
<p>{user?.bio}</p>
</div>
);
};