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
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill react-patternsAssembled 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.