react
How to Install
Claude Code:
git clone --depth 1 https://github.com/mwathiben/hush-private-bookmarks.git && cp hush-private-bookmarks/.claude/skills/react ~/.claude/skills/react -r---
name: react
description: Core React 19 patterns including hooks, Suspense, lazy loading, component structure, TypeScript best practices, and performance optimization. Use when working with React components, hooks, lazy loading, Suspense boundaries, or React-specific TypeScript patterns.
---
# React Core Patterns
## Purpose
Essential React 19 patterns for building modern applications with hooks, Suspense, lazy loading, and TypeScript.
**Note**: React 19 (released December 2024) breaking changes:
- `forwardRef` no longer needed - pass `ref` as a prop directly
- `propTypes` removed (silently ignored)
- New JSX transform required
- `React.FC` type discouraged - use direct function components instead
## When to Use This Skill
- Creating React components
- Using React hooks (useState, useEffect, useCallback, useMemo)
- Implementing lazy loading and code splitting
- Working with Suspense boundaries
- React-specific TypeScript patterns
- Performance optimization with React
---
## Quick Start
### Component Structure Template
```typescript
import { useState, useCallback } from 'react';
interface Props {
userId: string;
onUpdate?: (data: UserData) => void;
}
interface UserData {
name: string;
email: string;
}
function UserProfile({ userId, onUpdate }: Props) {
const [data, setData] = useState(null);
const handleUpdate = useCallback((newData: UserData) => {
setData(newData);
onUpdate?.(newData);
}, [onUpdate]);
return (
` with fallback
- [ ] Default export at bottom
- [ ] No conditional hooks (hooks must be called in same order)
- [ ] Pass `ref` as a prop (no `forwardRef` needed in React 19)
---
## Core Hooks Patterns
### useState
```typescript
// Simple state
const [count, setCount] = useState(0);
// Object state
const [user, setUser] = useState(null);
// Array state
const [items, setItems] = useState- ([]);
// Functional updates when depending on previous state
setCount(prev => prev + 1);
setItems(prev => [...prev, newItem]);
```
### useCallback
```typescript
// Wrap functions passed to child components
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []); // Empty deps if no dependencies
// With dependencies
const handleUpdate = useCallback((data: FormData) => {
apiCall(userId, data);
}, [userId]); // Re-create when userId changes
```
### useMemo
```typescript
// Expensive computation
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.score - b.score);
}, [items]);
// Derived state
const totalPrice = useMemo(() => {
return cart.reduce((sum, item) => sum + item.price, 0);
}, [cart]);
```
### useEffect
```typescript
// Run once on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependency changes
useEffect(() => {
if (userId) {
loadUserData(userId);
}
}, [userId]);
// Cleanup
useEffect(() => {
const subscription = subscribe(userId);
return () => subscription.unsubscribe();
}, [userId]);
```
---
## Lazy Loading & Code Splitting
### Basic Lazy Loading
```typescript
import React, { Suspense } from 'react';
// Lazy load heavy component
const HeavyChart = React.lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
Loading chart... }>
);
}
```
### Multiple Lazy Components
```typescript
const AdminPanel = React.lazy(() => import('./AdminPanel'));
const UserSettings = React.lazy(() => import('./UserSettings'));
const Reports = React.lazy(() => import('./Reports'));
function App() {
return (
{/* Component content */}
);
}
export default UserProfile;
```
### Component Checklist
Creating a React component? Follow this:
- [ ] Use function components with typed props (not `React.FC`)
- [ ] Define interfaces for Props and local state
- [ ] Use `useCallback` for event handlers passed to children
- [ ] Use `useMemo` for expensive computations
- [ ] Lazy load if heavy component: `lazy(() => import())`
- [ ] Wrap lazy components in `Dashboard
{user.name}
{data.map(...)}
;
}
```
### Lists and Keys
```typescript
// Always use stable keys
{items.map(item => (