About react-19
A reference skill for developers building with React 19. It explains new hooks and form actions alongside Server Components, Server Actions, and the React Compiler, with migration and troubleshooting guidance.
Key features
- 01Use Actions for asynchronous mutations
- 02Manage forms with useActionState and useFormStatus
- 03Apply optimistic UI updates with useOptimistic
- 04Use Server Components and Server Actions
- 05Understand React Compiler and ref as prop
Use cases
- 01Build forms with pending and error states
- 02Show immediate feedback during data updates
- 03Structure applications with server-rendered components
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill react-19After installing, type/react-19in Claude Code to confirm it loaded.
Bundled fileshooks.mdserver.md
Details
From the project READMEReact 19
Overview of New Features
| Feature | Description |
|---|---|
| Actions | Async functions in transitions for data mutations |
useActionState | Handle action state with pending/error |
useFormStatus | Access parent form status |
useOptimistic | Optimistic UI updates |
use() | Read resources in render (Promises, Context) |
| Server Components | Components that render on server |
| Server Actions | Server functions called from client |
| React Compiler | Automatic memoization |
| Ref as prop | Pass ref directly without forwardRef |
Actions
Actions are async functions used in transitions to handle mutations:
import { useTransition } from 'react';
function UpdateProfile() {
const [isPending, startTransition] = useTransition();
async function handleSubmit(formData: FormData) {
startTransition(async () => {
const result = await updateProfile(formData);
if (result.error) {
showToast(result.error);
}
});
}
return (
<form action={handleSubmit}>
<input name="name" type="text" />
<button type="submit" disabled={isPending}>
{isPending ? 'Saving...' : 'Save'}
</button>
</form>
);
}
useActionState
Replaces the experimental useFormState. Manages form action state including pending status:
import { useActionState } from 'react';
interface FormState {
message: string | null;
errors: Record<string, string[]>;
}
async function createPost(prevState: FormState, formData: FormData): Promise<FormState> {
const title = formData.get('title') as string;
if (!title || title.length < 3) {
return { message: null, errors: { title: ['Title must be at least 3 characters'] } };
}
try {
await savePost({ title });
return { message: 'Post created!', errors: {} };
} catch (error) {
return { message: 'Failed to create post', errors: {} };
}
}
function CreatePostForm() {
const [state, formAction, isPending] = useActionState(createPost, {
message: null,
errors: {}
});
return (
<form action={formAction}>
<input name="title" type="text" />
{state.errors.title && <p className="error">{state.errors.title[0]}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create Post'}
</button>
</form>
);
}
useFormStatus
Access the status of a parent <form> from any child component:
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data, method, action } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
// Usage - must be child of <form>
function ContactForm() {
return (
<form action={submitContact}>
<input name="email" type="email" required />
<SubmitButton />
</form>
);
}
Reusable Form Components
'use client';
import { useFormStatus } from 'react-dom';
export function FormButton({ children, pendingText = 'Submitting...', ...props }) {
const { pending } = useFormStatus();
return (
<button {...props} disabled={pending || props.disabled}>
{pending ? pendingText : children}
</button>
);
}
Full Reference: See hooks.md for useOptimistic, use() hook, Ref handling, Document Metadata.
Server Components & Server Actions
// app/users/page.tsx (Server Component by default)
export default async function UsersPage() {
const users = await fetchUsers();
return <UserList users={users} />;
}
// app/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
redirect(`/posts`);
}
Full Reference: See server.md for Server Components patterns, React Compiler, Migration Guide.
Best Practices
- ✅ Use Actions for form submissions and mutations
- ✅ Use
useOptimisticfor immediate feedback - ✅ Use
use()instead of useEffect for data fetching - ✅ Let React Compiler handle memoization
- ✅ Use Server Components for static content
- ✅ Use Server Actions for secure mutations
- ❌ Don't create Promises inside components for
use() - ❌ Don't mix client/server code without proper boundaries
When NOT to Use This Skill
- React 18 and earlier features - Use
reactskill instead - General form handling - Use
react-formsorreact-hook-formskills - Performance optimization - Use
react-performanceskill
Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Creating Promises in render for use() | New promise every render | Create promise outside component |
| Using useFormState instead of useActionState | Deprecated in React 19 | Use useActionState with isPending |
| Missing 'use server' directive | Code runs on client | Add 'use server' at top of file |
| Not validating Server Action inputs | Security vulnerability | Always validate with Zod |
Quick Troubleshooting
| Issue | Likely Cause | Fix |
|---|---|---|
| Action not working | Missing action attribute | Add action={serverAction} to form |
| isPending not available | Using old useFormState | Switch to useActionState |
| Serialization errors | Passing functions/classes | Only pass serializable data |
| ref not working | Using old forwardRef pattern | Pass ref as regular prop |
Reference Files
| File | Content |
|---|---|
| hooks.md | useOptimistic, use(), Ref handling, Metadata |
| server.md | Server Components, Server Actions, Compiler, Migration |