Files
nixos-config/home/common/programs/claude/skills/react-patterns/SKILL.md
T
Miguel Palhas b100de6f87 claude commands
2026-02-10 09:36:55 +00:00

5.0 KiB

description
description
React component patterns with TypeScript examples. Composition, compound components, custom hooks, Context+Reducer state, memoization, code splitting, virtualization, error boundaries, accessibility, and animations.

Frontend Development Patterns

Modern frontend patterns for React, Next.js, and performant user interfaces.

Component Patterns

Composition Over Inheritance

interface CardProps {
  children: React.ReactNode
  variant?: 'default' | 'outlined'
}

export function Card({ children, variant = 'default' }: CardProps) {
  return <div className={`card card-${variant}`}>{children}</div>
}

export function CardHeader({ children }: { children: React.ReactNode }) {
  return <div className="card-header">{children}</div>
}

export function CardBody({ children }: { children: React.ReactNode }) {
  return <div className="card-body">{children}</div>
}

Compound Components

interface TabsContextValue {
  activeTab: string
  setActiveTab: (tab: string) => void
}

const TabsContext = createContext<TabsContextValue | undefined>(undefined)

export function Tabs({ children, defaultTab }: {
  children: React.ReactNode
  defaultTab: string
}) {
  const [activeTab, setActiveTab] = useState(defaultTab)
  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      {children}
    </TabsContext.Provider>
  )
}

export function Tab({ id, children }: { id: string, children: React.ReactNode }) {
  const context = useContext(TabsContext)
  if (!context) throw new Error('Tab must be used within Tabs')
  return (
    <button
      className={context.activeTab === id ? 'active' : ''}
      onClick={() => context.setActiveTab(id)}
    >
      {children}
    </button>
  )
}

Custom Hooks Patterns

Async Data Fetching Hook

export function useQuery<T>(
  key: string,
  fetcher: () => Promise<T>,
  options?: { onSuccess?: (data: T) => void; onError?: (error: Error) => void; enabled?: boolean }
) {
  const [data, setData] = useState<T | null>(null)
  const [error, setError] = useState<Error | null>(null)
  const [loading, setLoading] = useState(false)

  const refetch = useCallback(async () => {
    setLoading(true)
    setError(null)
    try {
      const result = await fetcher()
      setData(result)
      options?.onSuccess?.(result)
    } catch (err) {
      const error = err as Error
      setError(error)
      options?.onError?.(error)
    } finally {
      setLoading(false)
    }
  }, [fetcher, options])

  useEffect(() => {
    if (options?.enabled !== false) refetch()
  }, [key, refetch, options?.enabled])

  return { data, error, loading, refetch }
}

Debounce Hook

export function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value)
  useEffect(() => {
    const handler = setTimeout(() => setDebouncedValue(value), delay)
    return () => clearTimeout(handler)
  }, [value, delay])
  return debouncedValue
}

State Management: Context + Reducer

type Action =
  | { type: 'SET_ITEMS'; payload: Item[] }
  | { type: 'SELECT_ITEM'; payload: Item }
  | { type: 'SET_LOADING'; payload: boolean }

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'SET_ITEMS': return { ...state, items: action.payload }
    case 'SELECT_ITEM': return { ...state, selectedItem: action.payload }
    case 'SET_LOADING': return { ...state, loading: action.payload }
    default: return state
  }
}

Performance Optimization

Memoization

  • useMemo for expensive computations
  • useCallback for functions passed to children
  • React.memo for pure components

Code Splitting & Lazy Loading

const HeavyChart = lazy(() => import('./HeavyChart'))
<Suspense fallback={<ChartSkeleton />}><HeavyChart data={data} /></Suspense>

Virtualization for Long Lists

Use @tanstack/react-virtual for lists with many items.

Error Boundary Pattern

export class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean; error: Error | null }
> {
  state = { hasError: false, error: null }
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error }
  }
  render() {
    if (this.state.hasError) {
      return <div><h2>Something went wrong</h2><p>{this.state.error?.message}</p></div>
    }
    return this.props.children
  }
}

Animation: Framer Motion

<AnimatePresence>
  {items.map(item => (
    <motion.div
      key={item.id}
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      exit={{ opacity: 0, y: -20 }}
      transition={{ duration: 0.3 }}
    >
      <ItemCard item={item} />
    </motion.div>
  ))}
</AnimatePresence>

Accessibility

  • Semantic HTML first (button, nav, main, article)
  • Keyboard navigation: ArrowDown/Up, Enter, Escape
  • Focus management for modals (save/restore focus)
  • ARIA attributes only when semantic HTML is insufficient

$ARGUMENTS