Use this skill when building React components, managing state with hooks, optimizing rendering performance, writing custom hooks, or structuring React 19 applications with TypeScript and Tailwind CSS. Also use when reviewing React code for best practices, fixing re-render issues, or designing component APIs.
Expert guidance for building React 19 applications with TypeScript.
interface UserCardProps {
user: User
onSelect: (id: string) => void
variant?: 'compact' | 'detailed'
}
function UserCard({ user, onSelect, variant = 'compact' }: UserCardProps) {
return (
<article className="user-card" data-variant={variant}>
<h3>{user.name}</h3>
{variant === 'detailed' && <p>{user.bio}</p>}
<button onClick={() => onSelect(user.id)}>Select</button>
</article>
)
}
use and encapsulate reusable logicuseCallback only when passing callbacks to memoized childrenuseMemo only for expensive computations, not as a defaultfunction useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay)
return () => clearTimeout(timer)
}, [value, delay])
return debounced
}
useState for local stateuseReducer for complex state with multiple sub-values// useReducer for complex state
type Action =
| { type: 'add'; item: Item }
| { type: 'remove'; id: string }
| { type: 'toggle'; id: string }
function cartReducer(state: CartState, action: Action): CartState {
switch (action.type) {
case 'add':
return { ...state, items: [...state.items, action.item] }
case 'remove':
return { ...state, items: state.items.filter(i => i.id !== action.id) }
case 'toggle':
return {
...state,
items: state.items.map(i =>
i.id === action.id ? { ...i, selected: !i.selected } : i
),
}
}
}
React.memo() only for components that re-render often with same propskey prop correctly — stable, unique identifiers, never array index for dynamic listsReact.lazy() and <Suspense>@next/bundle-analyzer or rollup-plugin-visualizerstrict: true in tsconfig — no exceptionsany — use unknown and narrow with type guards// Discriminated union for polymorphic components
type ButtonProps =
| { variant: 'link'; href: string; onClick?: never }
| { variant: 'button'; onClick: () => void; href?: never }
| { variant: 'submit'; onClick?: never; href?: never }
function Button(props: ButtonProps) {
switch (props.variant) {
case 'link':
return <a href={props.href}>Link</a>
case 'button':
return <button onClick={props.onClick}>Click</button>
case 'submit':
return <button type="submit">Submit</button>
}
}
// Typed event handlers
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
// submit logic
}
cn() utility (clsx + tailwind-merge) for conditional classestailwind.config.ts — colors, spacing, fontsimport { cn } from '@/lib/utils'
function Badge({ variant, children }: BadgeProps) {
return (
<span
className={cn(
'inline-flex items-center rounded-full px-2 py-1 text-xs font-medium',
variant === 'success' && 'bg-green-100 text-green-800',
variant === 'error' && 'bg-red-100 text-red-800',
variant === 'warning' && 'bg-yellow-100 text-yellow-800'
)}
>
{children}
</span>
)
}
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<p>Something went wrong</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
)
}
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer