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: Use when creating UI components in React - functional components, hooks, custom hooks, or component composition patterns. NOT when backend logic, API routes, or non-React frameworks are involved. Triggers: "create component", "build widget", "KPI card", "form", "modal", "custom hook", "useContext", "useState", "useEffect".4---56# React Component Skill78## Overview910Expert guidance for building React functional components, hooks, and composition patterns. Focuses on TypeScript, performance, accessibility, and modern React best practices.1112## When This Skill Applies1314This skill triggers when users request:15- **UI Components**: "Create a student KPI card", "Build a modal", "Form component"16- **Hooks**: "Custom attendance hook", "useContext for theme", "useState for data"17- **Patterns**: "Component composition", "HOC", "render props"18- **ERP Widgets**: KPI cards, forms, data tables, dashboards1920## Core Rules2122### 1. Functional Components Always2324```typescript25// ✅ GOOD: Functional component with TypeScript26interface StudentKPICardProps {27 studentName: string;28 attendance: number;29 loading?: boolean;30}3132export const StudentKPICard = React.memo(({ studentName, attendance, loading = false }: StudentKPICardProps) => {33 const [isHovered, setIsHovered] = useState(false);3435 if (loading) return <LoadingSkeleton />;3637 return <div>{/* content */}</div>;38});39```4041**Requirements:**42- Always use functional components (no class components)43- Type all props interfaces explicitly44- Use `React.memo` for components receiving same props frequently45- Use `React.forwardRef` when ref forwarding is needed4647### 2. Hooks Usage4849```typescript50// ✅ GOOD: Proper hook usage with cleanup51export const AttendanceMonitor = ({ studentId }: { studentId: string }) => {52 const [data, setData] = useState(null);53 const [error, setError] = useState<string | null>(null);5455 useEffect(() => {56 let isMounted = true;57 const fetchAttendance = async () => {58 try {59 const result = await api.getAttendance(studentId);60 if (isMounted) setData(result);61 } catch (err) {62 if (isMounted) setError(err.message);63 }64 };65 fetchAttendance();6667 return () => {68 isMounted = false;69 };70 }, [studentId]);7172 return /* JSX */;73};74```7576**Requirements:**77- `useState`: For local component state only78- `useEffect`: For side-effects with proper cleanup functions79- `useContext`: For global state (theme, auth, language)80- Always include all dependencies in dependency array81- Use `useCallback`/`useMemo` for expensive operations in lists8283### 3. Custom Hooks8485```typescript86// ✅ GOOD: Custom hook with proper types87interface UseAttendanceResult {88 data: AttendanceData | null;89 loading: boolean;90 error: string | null;91 refetch: () => Promise<void>;92}9394export const useAttendance = (studentId: string): UseAttendanceResult => {95 const [data, setData] = useState<AttendanceData | null>(null);96 const [loading, setLoading] = useState(true);97 const [error, setError] = useState<string | null>(null);9899 const fetchAttendance = useCallback(async () => {100 setLoading(true);101 try {102 const result = await api.getAttendance(studentId);103 setData(result);104 setError(null);105 } catch (err) {106 setError(err.message);107 } finally {108 setLoading(false);109 }110 }, [studentId]);111112 useEffect(() => {113 fetchAttendance();114 }, [fetchAttendance]);115116 return { data, loading, error, refetch: fetchAttendance };117};118```119120**Requirements:**121- All custom hooks must start with `use` prefix122- Extract reusable logic into hooks123- Return typed interfaces for hook results124- Include loading, error states, and retry mechanisms125- Keep hooks focused on single responsibility126127### 4. Component Composition128129```typescript130// ✅ GOOD: Composition via children prop131interface CardProps {132 title: string;133 children: React.ReactNode;134 footer?: React.ReactNode;135}136137export const Card = ({ title, children, footer }: CardProps) => (138 <div className="card">139 <h2>{title}</h2>140 <div className="card-content">{children}</div>141 {footer && <div className="card-footer">{footer}</div>}142 </div>143);144145// Usage146<Card title="Student Info" footer={<Button>Save</Button>}>147 <StudentDetails />148</Card>149150// ✅ GOOD: Higher-order component pattern151export const withLoading = <P extends object>(WrappedComponent: React.ComponentType<P>) => {152 return (props: P & { loading?: boolean }) => {153 const { loading = false, ...rest } = props;154 if (loading) return <LoadingSpinner />;155 return <WrappedComponent {...(rest as P)} />;156 };157};158```159160**Requirements:**161- Prefer composition over inheritance162- Use `children` prop for flexible content163- Use render props when component needs to share data164- Use HOCs sparingly and with proper TypeScript types165- Keep components small and focused (single responsibility)166167## Output Requirements168169### Code Files1701711. **Component file** (`ComponentName.tsx`):172 - Functional component with typed props173 - Hooks applied correctly174 - Exported as named export175 - Default export when appropriate1761772. **Test file** (`ComponentName.test.tsx`):178 - Jest/Vitest + React Testing Library179 - Test component renders with props180 - Test user interactions181 - Test loading/error states182 - Accessibility tests1831843. **Storybook file** (`ComponentName.stories.tsx`):185 - Default story186 - Variant stories (loading, error, different states)187 - Props table for documentation188189### Integration Requirements190191- **shadcn/ui**: Use existing shadcn components when available192- **Accessibility**: Follow WCAG 2.1 AA guidelines (use @ui-ux-designer for a11y audit)193- **Styling**: Tailwind CSS with design system tokens194- **i18n**: Prepare components for internationalization (no hardcoded text)195196### Documentation197198- **PHR**: Create Prompt History Record for each component development199- **ADR**: Document hook pattern decisions for complex custom hooks200- **Comments**: Add comments only for non-obvious logic201202## Workflow2032041. **Understand Requirements**205 - Clarify component purpose, props, and interactions206 - Identify state needs (local vs global)207 - Determine reusability potential2082092. **Design Props Interface**210 - Define TypeScript interface for all props211 - Mark optional props with `?`212 - Use discriminated unions for variant types2132143. **Implement Component**215 - Write functional component216 - Apply hooks with proper dependencies217 - Handle loading/error states218 - Ensure accessibility (ARIA attributes)2192204. **Test Component**221 - Write unit tests for all code paths222 - Test user interactions223 - Verify accessibility2242255. **Create Stories**226 - Document component with Storybook227 - Show all variants and states228 - Add controls for interactive exploration229230## Quality Checklist231232Before completing any component:233234- [ ] **React 19+ Hooks Rules**: No dependency issues, proper cleanup in useEffect235- [ ] **TypeScript Props**: Exhaustive type definitions, no `any` types236- [ ] **Custom Hooks**: All prefixed with 'use', single responsibility237- [ ] **Composition**: Used over inheritance, flexible via children/render props238- [ ] **Performance**: `useCallback`/`useMemo` for expensive operations in lists239- [ ] **Accessibility**: Proper ARIA labels, keyboard navigation support240- [ ] **Error Handling**: Graceful error states, no console errors241- [ ] **Loading States**: Clear loading indicators for async operations242- [ ] **Tests**: Unit tests cover all branches and interactions243- [ ] **Stories**: Storybook stories document component usage244245## Common Patterns246247### Data Fetching Component248249```typescript250export const StudentList = () => {251 const { data: students, loading, error } = useStudents();252253 if (loading) return <LoadingSkeleton count={5} />;254 if (error) return <ErrorState message={error} />;255256 return (257 <ul>258 {students?.map((student) => (259 <StudentItem key={student.id} student={student} />260 ))}261 </ul>262 );263};264```265266### Form Component with Validation267268```typescript269interface FormValues {270 name: string;271 email: string;272}273274export const StudentForm = () => {275 const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();276277 const onSubmit = async (data: FormValues) => {278 await api.createStudent(data);279 };280281 return (282 <form onSubmit={handleSubmit(onSubmit)}>283 <Input {...register('name', { required: true })} error={errors.name} />284 <Input {...register('email', { required: true })} error={errors.email} />285 <Button type="submit">Save</Button>286 </form>287 );288};289```290291## References292293- React Documentation: https://react.dev294- TypeScript React Cheatsheet: https://react-typescript-cheatsheet.netlify.app295- React Testing Library: https://testing-library.com/docs/react-testing-library/intro296- shadcn/ui: https://ui.shadcn.com