Claude-initial-setup component-patterns

install
source · Clone the upstream repo
git clone https://github.com/VersoXBT/claude-initial-setup
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/VersoXBT/claude-initial-setup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/react-nextjs/component-patterns" ~/.claude/skills/versoxbt-claude-initial-setup-component-patterns && rm -rf "$T"
manifest: skills/react-nextjs/component-patterns/SKILL.md
source content

React Component Patterns

Advanced React component composition patterns for building flexible, reusable UI APIs.

When to Use

  • User is designing a reusable component API
  • User asks about compound components, render props, or HOCs
  • User needs to choose between controlled and uncontrolled components
  • User is refactoring components for better composition
  • User mentions forwardRef or ref forwarding

Core Patterns

Compound Components

Share implicit state between related components using React context. This gives consumers a declarative API without prop drilling.

import { createContext, useContext, useState, ReactNode } from 'react'

interface TabsContextType {
  activeTab: string
  setActiveTab: (id: string) => void
}

const TabsContext = createContext<TabsContextType | null>(null)

function useTabsContext() {
  const context = useContext(TabsContext)
  if (!context) {
    throw new Error('Tabs compound components must be used within <Tabs>')
  }
  return context
}

function Tabs({ defaultTab, children }: { defaultTab: string; children: ReactNode }) {
  const [activeTab, setActiveTab] = useState(defaultTab)
  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      <div role="tablist">{children}</div>
    </TabsContext.Provider>
  )
}

function TabTrigger({ id, children }: { id: string; children: ReactNode }) {
  const { activeTab, setActiveTab } = useTabsContext()
  return (
    <button role="tab" aria-selected={activeTab === id} onClick={() => setActiveTab(id)}>
      {children}
    </button>
  )
}

function TabContent({ id, children }: { id: string; children: ReactNode }) {
  const { activeTab } = useTabsContext()
  if (activeTab !== id) return null
  return <div role="tabpanel">{children}</div>
}

Tabs.Trigger = TabTrigger
Tabs.Content = TabContent
export { Tabs }

Usage:

<Tabs defaultTab="settings">
  <Tabs.Trigger id="profile">Profile</Tabs.Trigger>
  <Tabs.Trigger id="settings">Settings</Tabs.Trigger>
  <Tabs.Content id="profile"><ProfileForm /></Tabs.Content>
  <Tabs.Content id="settings"><SettingsForm /></Tabs.Content>
</Tabs>

Render Props

Delegate rendering to consumers while encapsulating behavior logic.

interface MouseTrackerProps {
  children: (position: { x: number; y: number }) => ReactNode
}

function MouseTracker({ children }: MouseTrackerProps) {
  const [position, setPosition] = useState({ x: 0, y: 0 })

  const handleMouseMove = (e: React.MouseEvent) => {
    setPosition({ x: e.clientX, y: e.clientY })
  }

  return <div onMouseMove={handleMouseMove}>{children(position)}</div>
}

// Usage
<MouseTracker>
  {({ x, y }) => <Tooltip style={{ left: x, top: y }}>Cursor here</Tooltip>}
</MouseTracker>

Higher-Order Components (HOC)

Wrap components to inject cross-cutting concerns. Prefer hooks for new code, but HOCs remain useful for class components and third-party integrations.

function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>) {
  const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component'

  function WithAuth(props: P) {
    const { user, isLoading } = useAuth()

    if (isLoading) return <Spinner />
    if (!user) return <Navigate to="/login" />

    return <WrappedComponent {...props} />
  }

  WithAuth.displayName = `withAuth(${displayName})`
  return WithAuth
}

const ProtectedDashboard = withAuth(Dashboard)

Controlled vs Uncontrolled Components

Controlled: parent owns state. Uncontrolled: component owns state internally. Support both with a flexible API.

interface InputProps {
  value?: string
  defaultValue?: string
  onChange?: (value: string) => void
}

function Input({ value: controlledValue, defaultValue = '', onChange }: InputProps) {
  const [internalValue, setInternalValue] = useState(defaultValue)
  const isControlled = controlledValue !== undefined
  const currentValue = isControlled ? controlledValue : internalValue

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const next = e.target.value
    if (!isControlled) {
      setInternalValue(next)
    }
    onChange?.(next)
  }

  return <input value={currentValue} onChange={handleChange} />
}

forwardRef with useImperativeHandle

Expose a limited imperative API to parent components via refs.

import { forwardRef, useImperativeHandle, useRef } from 'react'

interface VideoPlayerHandle {
  play: () => void
  pause: () => void
  seek: (time: number) => void
}

const VideoPlayer = forwardRef<VideoPlayerHandle, { src: string }>(({ src }, ref) => {
  const videoRef = useRef<HTMLVideoElement>(null)

  useImperativeHandle(ref, () => ({
    play: () => videoRef.current?.play(),
    pause: () => videoRef.current?.pause(),
    seek: (time: number) => {
      if (videoRef.current) videoRef.current.currentTime = time
    },
  }))

  return <video ref={videoRef} src={src} />
})

VideoPlayer.displayName = 'VideoPlayer'

Anti-Patterns

  • Prop drilling through 5+ levels -- Use compound components or context instead of passing props through intermediate components that do not use them.
  • God components -- A single component handling layout, data fetching, and business logic. Split into container/presentational or use hooks.
  • Overusing HOCs -- Stacking multiple HOCs creates wrapper hell and makes debugging difficult. Prefer hooks for new code.
  • Mixing controlled and uncontrolled without a clear API boundary -- Pick one default and document it. If supporting both, use the pattern above.

Quick Reference

PatternUse WhenComplexity
Compound ComponentsDeclarative multi-part UI (tabs, accordion, menu)Medium
Render PropsConsumer needs full control over renderingLow
HOCCross-cutting concerns on class componentsMedium
Controlled/UncontrolledForm inputs, toggles, any stateful elementLow
forwardRefParent needs imperative access to child DOM/APILow

Prefer composition over inheritance. Prefer hooks over HOCs for new code. Prefer compound components for multi-element UI widgets.