원클릭으로
react-patterns
React 19 patterns including Server Components, Actions, Suspense, hooks, and component composition
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
React 19 patterns including Server Components, Actions, Suspense, hooks, and component composition
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | react-patterns |
| description | React 19 patterns including Server Components, Actions, Suspense, hooks, and component composition |
use() reads values from Promises and Context directly in render. Unlike other hooks, it can be called inside conditionals and loops.
import { use } from 'react';
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise);
return <h1>{user.name}</h1>;
}
function ThemeButton() {
const theme = use(ThemeContext);
return <button style={{ background: theme.primary }}>Click</button>;
}
Wrap components that use use() with a Promise in a <Suspense> boundary.
// 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 <UserList users={users} />;
}
// app/users/UserList.tsx - Still a Server Component
export function UserList({ users }: { users: User[] }) {
return (
<ul>
{users.map(u => (
<li key={u.id}>
{u.name}
<DeleteButton userId={u.id} />
</li>
))}
</ul>
);
}
Push 'use client' as deep as possible. Only leaves that need interactivity should be Client Components.
// 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');
}
// app/posts/new/page.tsx
import { createPost } from '../actions';
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" required />
<textarea name="body" required />
<button type="submit">Create</button>
</form>
);
}
'use client';
import { useActionState } from 'react';
import { createUser } from './actions';
function SignupForm() {
const [state, formAction, isPending] = useActionState(createUser, {
errors: {},
message: '',
});
return (
<form action={formAction}>
<input name="email" />
{state.errors.email && <p>{state.errors.email}</p>}
<button disabled={isPending}>
{isPending ? 'Creating...' : 'Sign Up'}
</button>
{state.message && <p>{state.message}</p>}
</form>
);
}
'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 (
<form action={handleLike}>
<button type="submit">{optimisticCount} Likes</button>
</form>
);
}
import { Suspense } from 'react';
function Dashboard() {
return (
<div>
<Suspense fallback={<StatsSkeleton />}>
<StatsPanel />
</Suspense>
<div className="grid grid-cols-2">
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart />
</Suspense>
<Suspense fallback={<ListSkeleton />}>
<RecentActivity />
</Suspense>
</div>
</div>
);
}
Place Suspense boundaries around independent data-fetching units. Avoid wrapping the entire page in a single boundary (defeats the purpose of streaming).
'use client';
import { Component, type ReactNode } from 'react';
class ErrorBoundary extends Component<
{ fallback: ReactNode; children: ReactNode },
{ hasError: boolean }
> {
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.
function 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;
}
function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
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:
use[value, setter] or objects { data, error, loading }function Tabs({ children }: { children: ReactNode }) {
const [active, setActive] = useState(0);
return (
<TabsContext value={{ active, setActive }}>
<div role="tablist">{children}</div>
</TabsContext>
);
}
Tabs.Tab = function Tab({ index, children }: { index: number; children: ReactNode }) {
const { active, setActive } = use(TabsContext);
return (
<button
role="tab"
aria-selected={active === index}
onClick={() => setActive(index)}
>
{children}
</button>
);
};
Tabs.Panel = function Panel({ index, children }: { index: number; children: ReactNode }) {
const { active } = use(TabsContext);
if (active !== index) return null;
return <div role="tabpanel">{children}</div>;
};
// Usage
<Tabs>
<Tabs.Tab index={0}>Profile</Tabs.Tab>
<Tabs.Tab index={1}>Settings</Tabs.Tab>
<Tabs.Panel index={0}><ProfileForm /></Tabs.Panel>
<Tabs.Panel index={1}><SettingsForm /></Tabs.Panel>
</Tabs>
React.memo only after profiling confirms unnecessary re-rendersuseMemo/useCallback for expensive computations or stable references passed to memoized childrenkey to reset component state intentionallyRoute broad or ambiguous AgentKit SEO work to the right module while keeping context scoped. Use when a request spans multiple surfaces, asks for overall digital-presence strategy, involves provider or install architecture, needs agent-context planning, or the correct platform skill is unclear.
Persistent memory system for Claude Code. Two-layer architecture (hot cache + knowledge wiki), safety hooks, /close-day end-of-day synthesis. Zero external dependencies.
Claude-native deep research using DAG-based query planning, parallel subagent execution, and gap-driven iteration. No external API needed.
Web accessibility patterns for WCAG 2.2 compliance including ARIA, keyboard navigation, screen readers, and testing
Authentication and authorization patterns including OAuth2, JWT, RBAC, session management, and PKCE flows
AWS cloud patterns for Lambda, ECS, S3, DynamoDB, and Infrastructure as Code with CDK/Terraform