agentsclimarketplace

React patterns

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/react-patterns

When to activate: React 19, hooks, RSC, Server Components, Context, portals, error boundaries, performance optimizationFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill react-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.6 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it

React Patterns

Project Structure

src/
├── app/                  # Next.js App Router pages (or routes/)
├── components/
│   ├── ui/               # Headless / primitive UI atoms
│   ├── features/         # Feature-scoped components
│   └── layouts/          # Page shells
├── hooks/                # Custom hooks
├── lib/                  # Utilities, helpers
├── stores/               # Client state (Zustand / Jotai)
└── types/                # Shared TS types

Hooks Patterns

useCallback / useMemo

// useMemo: expensive pure computation
const filtered = useMemo(
  () => items.filter(i => i.active && i.name.includes(query)),
  [items, query]
)

// useCallback: stable reference for child prop / effect dep
const handleSubmit = useCallback(async (data: FormData) => {
  await mutate(data)
}, [mutate])

Custom Data Hook

function useUsers(filter: string) {
  return useQuery({
    queryKey: ['users', filter],
    queryFn: () => api.users.list({ filter }),
    staleTime: 60_000,
  })
}

Custom Event Hook

function useKeyDown(key: string, handler: () => void) {
  useEffect(() => {
    const listener = (e: KeyboardEvent) => {
      if (e.key === key) handler()
    }
    window.addEventListener('keydown', listener)
    return () => window.removeEventListener('keydown', listener)
  }, [key, handler])
}

Context

const ThemeContext = createContext<Theme | null>(null)

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<Theme>('light')
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  )
}

export function useTheme() {
  const ctx = useContext(ThemeContext)
  if (!ctx) throw new Error('useTheme must be inside ThemeProvider')
  return ctx
}

Compound Components

interface TabsCtxValue { active: string; setActive: (id: string) => void }
const TabsCtx = createContext<TabsCtxValue | null>(null)

function Tabs({ defaultValue, children }: { defaultValue: string; children: ReactNode }) {
  const [active, setActive] = useState(defaultValue)
  return <TabsCtx.Provider value={{ active, setActive }}>{children}</TabsCtx.Provider>
}

function Tab({ value, children }: { value: string; children: ReactNode }) {
  const { active, setActive } = useContext(TabsCtx)!
  return (
    <button role="tab" aria-selected={active === value} onClick={() => setActive(value)}>
      {children}
    </button>
  )
}

function TabPanel({ value, children }: { value: string; children: ReactNode }) {
  const { active } = useContext(TabsCtx)!
  if (active !== value) return null
  return <div role="tabpanel">{children}</div>
}

Tabs.Tab = Tab
Tabs.Panel = TabPanel

Error Boundaries

class ErrorBoundary extends Component<
  { fallback: ReactNode; children: ReactNode },
  { error: Error | null }
> {
  state = { error: null }
  static getDerivedStateFromError(e: Error) { return { error: e } }
  componentDidCatch(e: Error, info: ErrorInfo) { console.error(e, info) }
  render() {
    return this.state.error ? this.props.fallback : this.props.children
  }
}

Render Optimization

React.memo with custom comparator

const Row = memo(
  function Row({ item }: { item: Item }) {
    return <li>{item.name}</li>
  },
  (prev, next) => prev.item.id === next.item.id
)

Virtualization for large lists

import { useVirtualizer } from '@tanstack/react-virtual'

function VirtualList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null)
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 48,
  })

  return (
    <div ref={parentRef} style={{ height: 400, overflow: 'auto' }}>
      <div style={{ height: virtualizer.getTotalSize() }}>
        {virtualizer.getVirtualItems().map(row => (
          <div key={row.key} style={{ transform: `translateY(${row.start}px)`, position: 'absolute', width: '100%' }}>
            {items[row.index].name}
          </div>
        ))}
      </div>
    </div>
  )
}

React 19 Features

use() for promises

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise)  // suspends until resolved
  return <div>{user.name}</div>
}

Server Actions

'use server'
export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  await db.post.create({ data: { title } })
  revalidatePath('/posts')
}

// Component
<form action={createPost}>
  <input name="title" />
  <button type="submit">Create</button>
</form>

useOptimistic

function LikeButton({ postId, likes }: { postId: string; likes: number }) {
  const [optimisticLikes, addOptimistic] = useOptimistic(likes)
  async function handleLike() {
    addOptimistic(l => l + 1)
    await likePost(postId)
  }
  return <button onClick={handleLike}>{optimisticLikes} likes</button>
}

Portals

function Modal({ children, onClose }: { children: ReactNode; onClose: () => void }) {
  return createPortal(
    <div role="dialog" aria-modal>
      <button onClick={onClose}>Close</button>
      {children}
    </div>,
    document.getElementById('modal-root')!
  )
}

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,852. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.