# React Patterns

> React 19 patterns including Server Components, Actions, Suspense, hooks, and component composition

- **Type:** Skill
- **Install:** `agentstack add skill-rohitg00-awesome-claude-code-toolkit-react-patterns`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [rohitg00](https://agentstack.voostack.com/s/rohitg00)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [rohitg00](https://github.com/rohitg00)
- **Source:** https://github.com/rohitg00/awesome-claude-code-toolkit/tree/main/skills/react-patterns

## Install

```sh
agentstack add skill-rohitg00-awesome-claude-code-toolkit-react-patterns
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# React Patterns

## use() Hook (React 19)

`use()` reads values from Promises and Context directly in render. Unlike other hooks, it can be called inside conditionals and loops.

```tsx
import { use } from 'react';

function UserProfile({ userPromise }: { userPromise: Promise }) {
  const user = use(userPromise);
  return {user.name};
}

function ThemeButton() {
  const theme = use(ThemeContext);
  return Click;
}
```

Wrap components that use `use()` with a Promise in a `` boundary.

## Server Components

```tsx
// app/users/page.tsx - Server Component (default, no directive needed)
import { UserList } from './UserList';

export default async function UsersPage() {
  const users = await fetch('https://api.example.com/users', {
    next: { revalidate: 60 },
  }).then(r => r.json());

  return ;
}

// app/users/UserList.tsx - Still a Server Component
export function UserList({ users }: { users: User[] }) {
  return (
    
      {users.map(u => (
        
          {u.name}
          
        
      ))}
    
  );
}
```

Push `'use client'` as deep as possible. Only leaves that need interactivity should be Client Components.

## Server Actions

```tsx
// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string;
  const body = formData.get('body') as string;

  await db.insert(posts).values({ title, body });

  revalidatePath('/posts');
  redirect('/posts');
}
```

```tsx
// app/posts/new/page.tsx
import { createPost } from '../actions';

export default function NewPostPage() {
  return (
    
      
      
      Create
    
  );
}
```

## useActionState (React 19)

```tsx
'use client';

import { useActionState } from 'react';
import { createUser } from './actions';

function SignupForm() {
  const [state, formAction, isPending] = useActionState(createUser, {
    errors: {},
    message: '',
  });

  return (
    
      
      {state.errors.email && {state.errors.email}}
      
        {isPending ? 'Creating...' : 'Sign Up'}
      
      {state.message && {state.message}}
    
  );
}
```

## useOptimistic (React 19)

```tsx
'use client';

import { useOptimistic } from 'react';
import { likePost } from './actions';

function LikeButton({ count, postId }: { count: number; postId: string }) {
  const [optimisticCount, addOptimistic] = useOptimistic(count);

  async function handleLike() {
    addOptimistic(prev => prev + 1);
    await likePost(postId);
  }

  return (
    
      {optimisticCount} Likes
    
  );
}
```

## Suspense Boundaries

```tsx
import { Suspense } from 'react';

function Dashboard() {
  return (
    
      }>
        
      
      
        }>
          
        
        }>
          
        
      
    
  );
}
```

Place Suspense boundaries around independent data-fetching units. Avoid wrapping the entire page in a single boundary (defeats the purpose of streaming).

## Error Boundaries

```tsx
'use client';

import { Component, type ReactNode } from 'react';

class ErrorBoundary extends Component {
  state = { hasError: false };

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

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    reportError(error, info.componentStack);
  }

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

Or use Next.js `error.tsx` convention for route-level error handling.

## Custom Hooks

```tsx
function useDebounce(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

function useLocalStorage(key: string, initial: T) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initial;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}
```

Rules for custom hooks:
- Prefix with `use`
- Extract when logic is shared between 2+ components
- Keep hooks focused on a single concern
- Return tuples `[value, setter]` or objects `{ data, error, loading }`

## Compound Components

```tsx
function Tabs({ children }: { children: ReactNode }) {
  const [active, setActive] = useState(0);
  return (
    
      {children}
    
  );
}

Tabs.Tab = function Tab({ index, children }: { index: number; children: ReactNode }) {
  const { active, setActive } = use(TabsContext);
  return (
     setActive(index)}
    >
      {children}
    
  );
};

Tabs.Panel = function Panel({ index, children }: { index: number; children: ReactNode }) {
  const { active } = use(TabsContext);
  if (active !== index) return null;
  return {children};
};

// Usage

  Profile
  Settings
  
  

```

## Performance Rules

- Avoid creating objects/arrays in JSX props (causes re-renders)
- Use `React.memo` only after profiling confirms unnecessary re-renders
- Prefer `useMemo`/`useCallback` for expensive computations or stable references passed to memoized children
- Use `key` to reset component state intentionally
- Colocate state: keep state as close to where it is used as possible
- Avoid prop drilling beyond 2 levels; use Context or composition instead

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [rohitg00](https://github.com/rohitg00)
- **Source:** [rohitg00/awesome-claude-code-toolkit](https://github.com/rohitg00/awesome-claude-code-toolkit)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-rohitg00-awesome-claude-code-toolkit-react-patterns
- Seller: https://agentstack.voostack.com/s/rohitg00
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
