React 19 patterns for components, hooks, Server Components, and data fetching. Use when writing React components, managing state with hooks, implementing Suspense boundaries, optimizing renders with proper memoization, or building Server/Client component hierarchies.
React patterns evolve with each major version. React 19 introduced the use() hook for promise handling and formalized Server Component boundaries with clearer client/server semantics. Common pitfalls include:
React 19 improves these patterns: use() hook for consuming promises, Server Components for data fetching without extra requests, and useTransition() for non-blocking updates.
use() hook calls with Suspense boundaries for loading fallbackSee rules index for detailed patterns.
User: "Refactor this React component to reduce re-renders and clarify hook usage."
Expected behavior: Use tech-react guidance, follow its workflow, and return actionable output.
User: "I'm fetching data on the client with useEffect. How should I refactor this with Server Components?"
Expected behavior: Move data fetch to Server Component, pass promise to Client Component via use() hook, wrap with Suspense boundary.
User: "Write a Bash script to package release artifacts."
Expected behavior: Do not prioritize tech-react; choose a more relevant skill or proceed without it.
enabled option in data hooks to skip executionuse() suspendsuse()<Suspense> is a parent of the component, not a siblingkey={index} to key={item.id} with unique property from dataIncorrect:
function UserProfile({ userId }: { userId: string | null }) {
if (!userId) {
return <div>Select a user</div>;
}
// Hook called conditionally - React can't track it!
const [profile, setProfile] = useState(null);
return <div>{profile?.name}</div>;
}Correct:
function UserProfile({ userId }: { userId: string | null }) {
const [profile, setProfile] = useState(null);
// Early return AFTER hooks
if (!userId) {
return <div>Select a user</div>;
}
return <div>{profile?.name}</div>;
}Incorrect:
function Comments({ id }: { id: string }) {
const [comments, setComments] = useState([]);
useEffect(() => {
fetchComments(id).then(setComments);
}, [id]);
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>;
}Correct:
function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
const comments = use(commentsPromise);
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>;
}
function CommentsSection({ id }: { id: string }) {
return (
<Suspense fallback={<div>Loading comments...</div>}>
<Comments commentsPromise={fetchComments(id)} />
</Suspense>
);
}Incorrect:
// BAD: Server Component with useState
async function NotesPage() {
const [selectedNote, setSelectedNote] = useState(null); // ERROR: Can't use state
const notes = await db.notes.getAll();
return (
<div>
{notes.map(note => (
<button onClick={() => setSelectedNote(note)}>
{note.title}
</button>
))}
</div>
);
}Correct:
// Server Component - fetch data
async function NotesPage() {
const notes = await db.notes.getAll();
return (
<div>
<NotesList notes={notes} />
</div>
);
}
// Client Component - handle interaction
"use client";
function NotesList({ notes }: { notes: Note[] }) {
const [selectedNote, setSelectedNote] = useState<Note | null>(null);
return (
<div>
{notes.map(note => (
<button
key={note.id}
onClick={() => setSelectedNote(note)}
>
{note.title}
</button>
))}
</div>
);
}ravnhq/ai-toolkit
e6237ae
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.