React Component Skill
Overview
Expert guidance for building React functional components, hooks, and composition patterns. Focuses on TypeScript, performance, accessibility, and modern React best practices.
When This Skill Applies
This skill triggers when users request:
- UI Components: "Create a student KPI card", "Build a modal", "Form component"
- Hooks: "Custom attendance hook", "useContext for theme", "useState for data"
- Patterns: "Component composition", "HOC", "render props"
- ERP Widgets: KPI cards, forms, data tables, dashboards
Core Rules
1. Functional Components Always
// ✅ GOOD: Functional component with TypeScript
interface StudentKPICardProps {
studentName: string;
attendance: number;
loading?: boolean;
}
export const StudentKPICard = React.memo(({ studentName, attendance, loading = false }: StudentKPICardProps) => {
const [isHovered, setIsHovered] = useState(false);
if (loading) return <LoadingSkeleton />;
return <div>{/* content */}</div>;
});
Requirements:
- Always use functional components (no class components)
- Type all props interfaces explicitly
- Use
React.memo for components receiving same props frequently
- Use
React.forwardRef when ref forwarding is needed
2. Hooks Usage
// ✅ GOOD: Proper hook usage with cleanup
export const AttendanceMonitor = ({ studentId }: { studentId: string }) => {
const [data, setData] = useState(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let isMounted = true;
const fetchAttendance = async () => {
try {
const result = await api.getAttendance(studentId);
if (isMounted) setData(result);
} catch (err) {
if (isMounted) setError(err.message);
}
};
fetchAttendance();
return () => {
isMounted = false;
};
}, [studentId]);
return /* JSX */;
};
Requirements:
useState: For local component state only
useEffect: For side-effects with proper cleanup functions
useContext: For global state (theme, auth, language)
- Always include all dependencies in dependency array
- Use
useCallback/useMemo for expensive operations in lists
3. Custom Hooks
// ✅ GOOD: Custom hook with proper types
interface UseAttendanceResult {
data: AttendanceData | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
export const useAttendance = (studentId: string): UseAttendanceResult => {
const [data, setData] = useState<AttendanceData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchAttendance = useCallback(async () => {
setLoading(true);
try {
const result = await api.getAttendance(studentId);
setData(result);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [studentId]);
useEffect(() => {
fetchAttendance();
}, [fetchAttendance]);
return { data, loading, error, refetch: fetchAttendance };
};
Requirements:
- All custom hooks must start with
use prefix
- Extract reusable logic into hooks
- Return typed interfaces for hook results
- Include loading, error states, and retry mechanisms
- Keep hooks focused on single responsibility
4. Component Composition
// ✅ GOOD: Composition via children prop
interface CardProps {
title: string;
children: React.ReactNode;
footer?: React.ReactNode;
}
export const Card = ({ title, children, footer }: CardProps) => (
<div className="card">
<h2>{title}</h2>
<div className="card-content">{children}</div>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
// Usage
<Card title="Student Info" footer={<Button>Save</Button>}>
<StudentDetails />
</Card>
// ✅ GOOD: Higher-order component pattern
export const withLoading = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
return (props: P & { loading?: boolean }) => {
const { loading = false, ...rest } = props;
if (loading) return <LoadingSpinner />;
return <WrappedComponent {...(rest as P)} />;
};
};
Requirements:
- Prefer composition over inheritance
- Use
children prop for flexible content
- Use render props when component needs to share data
- Use HOCs sparingly and with proper TypeScript types
- Keep components small and focused (single responsibility)
Output Requirements
Code Files
Component file (ComponentName.tsx):
- Functional component with typed props
- Hooks applied correctly
- Exported as named export
- Default export when appropriate
Test file (ComponentName.test.tsx):
- Jest/Vitest + React Testing Library
- Test component renders with props
- Test user interactions
- Test loading/error states
- Accessibility tests
Storybook file (ComponentName.stories.tsx):
- Default story
- Variant stories (loading, error, different states)
- Props table for documentation
Integration Requirements
- shadcn/ui: Use existing shadcn components when available
- Accessibility: Follow WCAG 2.1 AA guidelines (use @ui-ux-designer for a11y audit)
- Styling: Tailwind CSS with design system tokens
- i18n: Prepare components for internationalization (no hardcoded text)
Documentation
- PHR: Create Prompt History Record for each component development
- ADR: Document hook pattern decisions for complex custom hooks
- Comments: Add comments only for non-obvious logic
Workflow
Understand Requirements
- Clarify component purpose, props, and interactions
- Identify state needs (local vs global)
- Determine reusability potential
Design Props Interface
- Define TypeScript interface for all props
- Mark optional props with
?
- Use discriminated unions for variant types
Implement Component
- Write functional component
- Apply hooks with proper dependencies
- Handle loading/error states
- Ensure accessibility (ARIA attributes)
Test Component
- Write unit tests for all code paths
- Test user interactions
- Verify accessibility
Create Stories
- Document component with Storybook
- Show all variants and states
- Add controls for interactive exploration
Quality Checklist
Before completing any component:
Common Patterns
Data Fetching Component
export const StudentList = () => {
const { data: students, loading, error } = useStudents();
if (loading) return <LoadingSkeleton count={5} />;
if (error) return <ErrorState message={error} />;
return (
<ul>
{students?.map((student) => (
<StudentItem key={student.id} student={student} />
))}
</ul>
);
};
Form Component with Validation
interface FormValues {
name: string;
email: string;
}
export const StudentForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
const (data: FormValues) => {
await api.createStudent(data);
};
return (
<form
<Input {...register('name', { required: true })} error={errors.name} />
<Input {...register('email', { required: true })} error={errors.email} />
<Button type="submit">Save</Button>
</form>
);
};
References
1---2name: react-component3description: |4 Use when creating UI components in React - functional components, hooks, custom hooks, or component composition patterns.5 NOT when backend logic, API routes, or non-React frameworks are involved.6 Triggers: "create component", "build widget", "KPI card", "form", "modal", "custom hook", "useContext", "useState", "useEffect".7---89# React Component Skill1011## Overview1213Expert guidance for building React functional components, hooks, and composition patterns. Focuses on TypeScript, performance, accessibility, and modern React best practices.1415## When This Skill Applies1617This skill triggers when users request:18- **UI Components**: "Create a student KPI card", "Build a modal", "Form component"19- **Hooks**: "Custom attendance hook", "useContext for theme", "useState for data"20- **Patterns**: "Component composition", "HOC", "render props"21- **ERP Widgets**: KPI cards, forms, data tables, dashboards2223## Core Rules2425### 1. Functional Components Always2627```typescript28// ✅ GOOD: Functional component with TypeScript29interface StudentKPICardProps {30 studentName: string;31 attendance: number;32 loading?: boolean;33}3435export const StudentKPICard = React.memo(({ studentName, attendance, loading = false }: StudentKPICardProps) => {36 const [isHovered, setIsHovered] = useState(false);3738 if (loading) return <LoadingSkeleton />;3940 return <div>{/* content */}</div>;41});42```4344**Requirements:**45- Always use functional components (no class components)46- Type all props interfaces explicitly47- Use `React.memo` for components receiving same props frequently48- Use `React.forwardRef` when ref forwarding is needed4950### 2. Hooks Usage5152```typescript53// ✅ GOOD: Proper hook usage with cleanup54export const AttendanceMonitor = ({ studentId }: { studentId: string }) => {55 const [data, setData] = useState(null);56 const [error, setError] = useState<string | null>(null);5758 useEffect(() => {59 let isMounted = true;60 const fetchAttendance = async () => {61 try {62 const result = await api.getAttendance(studentId);63 if (isMounted) setData(result);64 } catch (err) {65 if (isMounted) setError(err.message);66 }67 };68 fetchAttendance();6970 return () => {71 isMounted = false;72 };73 }, [studentId]);7475 return /* JSX */;76};77```7879**Requirements:**80- `useState`: For local component state only81- `useEffect`: For side-effects with proper cleanup functions82- `useContext`: For global state (theme, auth, language)83- Always include all dependencies in dependency array84- Use `useCallback`/`useMemo` for expensive operations in lists8586### 3. Custom Hooks8788```typescript89// ✅ GOOD: Custom hook with proper types90interface UseAttendanceResult {91 data: AttendanceData | null;92 loading: boolean;93 error: string | null;94 refetch: () => Promise<void>;95}9697export const useAttendance = (studentId: string): UseAttendanceResult => {98 const [data, setData] = useState<AttendanceData | null>(null);99 const [loading, setLoading] = useState(true);100 const [error, setError] = useState<string | null>(null);101102 const fetchAttendance = useCallback(async () => {103 setLoading(true);104 try {105 const result = await api.getAttendance(studentId);106 setData(result);107 setError(null);108 } catch (err) {109 setError(err.message);110 } finally {111 setLoading(false);112 }113 }, [studentId]);114115 useEffect(() => {116 fetchAttendance();117 }, [fetchAttendance]);118119 return { data, loading, error, refetch: fetchAttendance };120};121```122123**Requirements:**124- All custom hooks must start with `use` prefix125- Extract reusable logic into hooks126- Return typed interfaces for hook results127- Include loading, error states, and retry mechanisms128- Keep hooks focused on single responsibility129130### 4. Component Composition131132```typescript133// ✅ GOOD: Composition via children prop134interface CardProps {135 title: string;136 children: React.ReactNode;137 footer?: React.ReactNode;138}139140export const Card = ({ title, children, footer }: CardProps) => (141 <div className="card">142 <h2>{title}</h2>143 <div className="card-content">{children}</div>144 {footer && <div className="card-footer">{footer}</div>}145 </div>146);147148// Usage149<Card title="Student Info" footer={<Button>Save</Button>}>150 <StudentDetails />151</Card>152153// ✅ GOOD: Higher-order component pattern154export const withLoading = <P extends object>(WrappedComponent: React.ComponentType<P>) => {155 return (props: P & { loading?: boolean }) => {156 const { loading = false, ...rest } = props;157 if (loading) return <LoadingSpinner />;158 return <WrappedComponent {...(rest as P)} />;159 };160};161```162163**Requirements:**164- Prefer composition over inheritance165- Use `children` prop for flexible content166- Use render props when component needs to share data167- Use HOCs sparingly and with proper TypeScript types168- Keep components small and focused (single responsibility)169170## Output Requirements171172### Code Files1731741. **Component file** (`ComponentName.tsx`):175 - Functional component with typed props176 - Hooks applied correctly177 - Exported as named export178 - Default export when appropriate1791802. **Test file** (`ComponentName.test.tsx`):181 - Jest/Vitest + React Testing Library182 - Test component renders with props183 - Test user interactions184 - Test loading/error states185 - Accessibility tests1861873. **Storybook file** (`ComponentName.stories.tsx`):188 - Default story189 - Variant stories (loading, error, different states)190 - Props table for documentation191192### Integration Requirements193194- **shadcn/ui**: Use existing shadcn components when available195- **Accessibility**: Follow WCAG 2.1 AA guidelines (use @ui-ux-designer for a11y audit)196- **Styling**: Tailwind CSS with design system tokens197- **i18n**: Prepare components for internationalization (no hardcoded text)198199### Documentation200201- **PHR**: Create Prompt History Record for each component development202- **ADR**: Document hook pattern decisions for complex custom hooks203- **Comments**: Add comments only for non-obvious logic204205## Workflow2062071. **Understand Requirements**208 - Clarify component purpose, props, and interactions209 - Identify state needs (local vs global)210 - Determine reusability potential2112122. **Design Props Interface**213 - Define TypeScript interface for all props214 - Mark optional props with `?`215 - Use discriminated unions for variant types2162173. **Implement Component**218 - Write functional component219 - Apply hooks with proper dependencies220 - Handle loading/error states221 - Ensure accessibility (ARIA attributes)2222234. **Test Component**224 - Write unit tests for all code paths225 - Test user interactions226 - Verify accessibility2272285. **Create Stories**229 - Document component with Storybook230 - Show all variants and states231 - Add controls for interactive exploration232233## Quality Checklist234235Before completing any component:236237- [ ] **React 19+ Hooks Rules**: No dependency issues, proper cleanup in useEffect238- [ ] **TypeScript Props**: Exhaustive type definitions, no `any` types239- [ ] **Custom Hooks**: All prefixed with 'use', single responsibility240- [ ] **Composition**: Used over inheritance, flexible via children/render props241- [ ] **Performance**: `useCallback`/`useMemo` for expensive operations in lists242- [ ] **Accessibility**: Proper ARIA labels, keyboard navigation support243- [ ] **Error Handling**: Graceful error states, no console errors244- [ ] **Loading States**: Clear loading indicators for async operations245- [ ] **Tests**: Unit tests cover all branches and interactions246- [ ] **Stories**: Storybook stories document component usage247248## Common Patterns249250### Data Fetching Component251252```typescript253export const StudentList = () => {254 const { data: students, loading, error } = useStudents();255256 if (loading) return <LoadingSkeleton count={5} />;257 if (error) return <ErrorState message={error} />;258259 return (260 <ul>261 {students?.map((student) => (262 <StudentItem key={student.id} student={student} />263 ))}264 </ul>265 );266};267```268269### Form Component with Validation270271```typescript272interface FormValues {273 name: string;274 email: string;275}276277export const StudentForm = () => {278 const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();279280 const onSubmit = async (data: FormValues) => {281 await api.createStudent(data);282 };283284 return (285 <form onSubmit={handleSubmit(onSubmit)}>286 <Input {...register('name', { required: true })} error={errors.name} />287 <Input {...register('email', { required: true })} error={errors.email} />288 <Button type="submit">Save</Button>289 </form>290 );291};292```293294## References295296- React Documentation: https://react.dev297- TypeScript React Cheatsheet: https://react-typescript-cheatsheet.netlify.app298- React Testing Library: https://testing-library.com/docs/react-testing-library/intro299- shadcn/ui: https://ui.shadcn.com