Agent skill

react

This skill should be used when the user asks to "create a React component", "use React hooks", "handle state", "implement forms", "use useOptimistic", "use useActionState", "create Server Components", "add interactivity", or discusses React patterns, component architecture, or state management. Always use the latest React version and modern patterns.

Stars 1
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/azlekov/my-claude-code/tree/main/skills/react

SKILL.md

React Development

This skill provides guidance for building applications with React, focusing on always using the latest version and modern patterns.

Philosophy: Prefer Server Components by default. Use modern hooks (useActionState, useOptimistic). Leverage the React Compiler for automatic optimization.

Quick Reference

Feature Modern Approach Legacy (Avoid)
Form State useActionState useFormState (deprecated)
Optimistic UI useOptimistic Manual state management
Promises in Render use() hook useEffect + useState
Context use(Context) useContext(Context)
Memoization React Compiler Manual useMemo, useCallback
Refs ref prop on functions forwardRef wrapper

Component Types

Server Components (Default in App Router)

tsx
// No directive needed - server by default
async function UserProfile({ userId }: { userId: string }) {
  const user = await db.users.find(userId)

  return (
    <div className="profile">
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  )
}

Server Components can:

  • Use async/await
  • Access databases directly
  • Read files from filesystem
  • Keep secrets server-side

Server Components cannot:

  • Use useState, useEffect
  • Add event handlers
  • Access browser APIs

Client Components

tsx
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  )
}

Use Client Components when you need:

  • Interactivity (onClick, onChange)
  • Browser APIs (localStorage, window)
  • React hooks (useState, useEffect)

Modern Hooks

useActionState

Handle form actions with loading and error states:

tsx
'use client'

import { useActionState } from 'react'

interface FormState {
  error: string | null
  success: boolean
}

async function submitForm(prevState: FormState, formData: FormData): Promise<FormState> {
  const email = formData.get('email') as string

  if (!email.includes('@')) {
    return { error: 'Invalid email', success: false }
  }

  await saveEmail(email)
  return { error: null, success: true }
}

export function EmailForm() {
  const [state, formAction, isPending] = useActionState(submitForm, {
    error: null,
    success: false
  })

  return (
    <form action={formAction}>
      <input
        name="email"
        type="email"
        disabled={isPending}
        placeholder="Enter email"
      />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Submitting...' : 'Submit'}
      </button>

      {state.error && <p className="text-red-500">{state.error}</p>}
      {state.success && <p className="text-green-500">Success!</p>}
    </form>
  )
}

useOptimistic

Provide instant UI feedback while async operations complete:

tsx
'use client'

import { useOptimistic, useTransition } from 'react'

interface Message {
  id: string
  text: string
  sending?: boolean
}

export function MessageList({
  messages,
  sendMessage
}: {
  messages: Message[]
  sendMessage: (text: string) => Promise<void>
}) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage: Message) => [...state, { ...newMessage, sending: true }]
  )

  const [, startTransition] = useTransition()

  async function handleSubmit(formData: FormData) {
    const text = formData.get('text') as string

    // Instantly show the message
    addOptimisticMessage({ id: `temp-${Date.now()}`, text })

    // Then actually send it
    startTransition(async () => {
      await sendMessage(text)
    })
  }

  return (
    <div>
      <ul>
        {optimisticMessages.map(msg => (
          <li
            key={msg.id}
            className={msg.sending ? 'opacity-50' : ''}
          >
            {msg.text}
            {msg.sending && <span className="ml-2">Sending...</span>}
          </li>
        ))}
      </ul>

      <form action={handleSubmit}>
        <input name="text" placeholder="Type a message" />
        <button type="submit">Send</button>
      </form>
    </div>
  )
}

use() Hook

Read promises and context directly in render:

tsx
import { use, Suspense } from 'react'

// Reading a promise
function UserName({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise)
  return <h1>{user.name}</h1>
}

export function UserProfile({ userId }: { userId: string }) {
  const userPromise = fetchUser(userId) // Start fetching

  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserName userPromise={userPromise} />
    </Suspense>
  )
}
tsx
// Reading context (replaces useContext)
import { ThemeContext } from './theme'

function ThemedButton() {
  const theme = use(ThemeContext)
  return <button className={theme.buttonClass}>Click me</button>
}

React Compiler

Enable automatic memoization without manual useMemo/useCallback:

typescript
// next.config.ts
const nextConfig = {
  experimental: {
    reactCompiler: true
  }
}

Before (manual memoization):

tsx
const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
  const processedData = useMemo(() => expensiveProcess(data), [data])
  const handleClick = useCallback(() => doSomething(data), [data])

  return <div onClick={handleClick}>{processedData}</div>
})

After (React Compiler handles it):

tsx
function ExpensiveComponent({ data }) {
  const processedData = expensiveProcess(data)
  const handleClick = () => doSomething(data)

  return <div onClick={handleClick}>{processedData}</div>
}

Component Patterns

Composition Over Props

tsx
// Instead of prop drilling
function Card({ title, subtitle, children, footer }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <p>{subtitle}</p>
      {children}
      <div>{footer}</div>
    </div>
  )
}

// Use composition
function Card({ children }) {
  return <div className="card">{children}</div>
}

Card.Header = function Header({ children }) {
  return <div className="card-header">{children}</div>
}

Card.Body = function Body({ children }) {
  return <div className="card-body">{children}</div>
}

Card.Footer = function Footer({ children }) {
  return <div className="card-footer">{children}</div>
}

// Usage
<Card>
  <Card.Header>
    <h2>Title</h2>
  </Card.Header>
  <Card.Body>Content here</Card.Body>
  <Card.Footer>
    <button>Action</button>
  </Card.Footer>
</Card>

Render Props

tsx
interface MousePosition {
  x: number
  y: number
}

function MouseTracker({
  children
}: {
  children: (position: MousePosition) => React.ReactNode
}) {
  const [position, setPosition] = useState({ x: 0, y: 0 })

  useEffect(() => {
    const handleMove = (e: MouseEvent) => {
      setPosition({ x: e.clientX, y: e.clientY })
    }
    window.addEventListener('mousemove', handleMove)
    return () => window.removeEventListener('mousemove', handleMove)
  }, [])

  return <>{children(position)}</>
}

// Usage
<MouseTracker>
  {({ x, y }) => <div>Mouse: {x}, {y}</div>}
</MouseTracker>

Server/Client Boundary

tsx
// Server Component (fetches data)
async function Dashboard() {
  const stats = await fetchStats()
  const activities = await fetchActivities()

  return (
    <div>
      <StatsCards stats={stats} />           {/* Server */}
      <InteractiveChart data={stats} />      {/* Client */}
      <ActivityFeed activities={activities} /> {/* Server */}
      <FilterPanel />                         {/* Client */}
    </div>
  )
}

// Client Component (interactive)
'use client'
function InteractiveChart({ data }) {
  const [range, setRange] = useState('7d')
  // Chart logic...
}

Refs in React 19

Direct Ref Prop (No More forwardRef)

tsx
// React 19: Direct ref prop
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />
}

// Usage
function Form() {
  const inputRef = useRef<HTMLInputElement>(null)

  return <Input ref={inputRef} placeholder="Name" />
}

Cleanup Functions

tsx
function Component() {
  const ref = useCallback((node: HTMLDivElement | null) => {
    if (node) {
      // Setup
      const observer = new IntersectionObserver(...)
      observer.observe(node)

      // Cleanup (new in React 19)
      return () => observer.disconnect()
    }
  }, [])

  return <div ref={ref}>Observed</div>
}

Context

Creating Context

tsx
import { createContext, use } from 'react'

interface User {
  id: string
  name: string
}

const UserContext = createContext<User | null>(null)

function UserProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null)

  return (
    <UserContext value={user}>
      {children}
    </UserContext>
  )
}

Consuming Context (Modern)

tsx
function UserProfile() {
  const user = use(UserContext)

  if (!user) return <div>Please log in</div>

  return <div>Hello, {user.name}</div>
}

Error Boundaries

tsx
'use client'

import { Component, type ReactNode } from 'react'

interface Props {
  children: ReactNode
  fallback: ReactNode
}

interface State {
  hasError: boolean
}

class ErrorBoundary extends Component<Props, State> {
  state = { hasError: false }

  static getDerivedStateFromError() {
    return { hasError: true }
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback
    }
    return this.props.children
  }
}

// Usage
<ErrorBoundary fallback={<div>Something went wrong</div>}>
  <RiskyComponent />
</ErrorBoundary>

Additional Resources

For detailed patterns, see reference files:

  • references/hooks.md - Complete hooks reference
  • references/server-components.md - RSC patterns and best practices

Expand your agent's capabilities with these related and highly-rated skills.

azlekov/my-claude-code

stripe

This skill should be used when the user asks to "integrate Stripe", "add payments", "create subscriptions", "handle webhooks", "usage-based billing", "per-seat pricing", "tiered plans", "checkout session", "customer portal", "sync Stripe data", "Stripe Sync Engine", "payment processing", "MRR analytics", "revenue reporting", or mentions 'Stripe', 'subscription', 'billing', 'webhook', 'checkout', 'metered billing', 'payment intent', 'stripe schema'. Automatically triggers for payment, subscription, and billing analytics work.

1 0
Explore
azlekov/my-claude-code

shadcn

This skill should be used when the user asks to "add a component", "use shadcn", "install Button", "create Dialog", "add Form", "use DataTable", "implement dark mode toggle", "use cn utility", or discusses UI components, component libraries, or accessible components. Always use the latest shadcn/ui version and modern patterns.

1 0
Explore
azlekov/my-claude-code

tailwindcss

This skill should be used when the user asks to "style with Tailwind", "add CSS", "configure theme", "use @theme", "add custom colors", "implement dark mode", "use container queries", "add responsive design", "use OKLCH colors", or discusses styling, theming, or visual design. Always use the latest Tailwind CSS version and modern patterns.

1 0
Explore
azlekov/my-claude-code

nextjs

This skill should be used when the user asks to "create a Next.js app", "build a page", "add routing", "implement server components", "add caching", "create API routes", "use server actions", "add metadata", "set up layouts", or discusses Next.js architecture, App Router, data fetching, or rendering strategies. Always use the latest Next.js version and modern patterns.

1 0
Explore
azlekov/my-claude-code

supabase-expert

This skill should be used when the user asks to "create a Supabase table", "write RLS policies", "set up Supabase Auth", "create Edge Functions", "configure Storage buckets", "use Supabase with Next.js", "migrate API keys", "implement row-level security", "create database functions", "set up SSR auth", or mentions 'Supabase', 'RLS', 'Edge Function', 'Storage bucket', 'anon key', 'service role', 'publishable key', 'secret key'. Automatically triggers when user mentions 'database', 'table', 'SQL', 'migration', 'policy'.

1 0
Explore
azlekov/my-claude-code

postgres-nanoid

This skill should be used when the user asks to "generate IDs", "create identifiers", "use nanoid", "add public_id", "prefixed identifiers", "short IDs", or discusses ID generation strategies, public vs internal IDs, or URL-friendly identifiers. Use nanoid for public identifiers and UUID for auth.users references.

1 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results