Skill de Dominio de React 19 — Una receta de skill para Claude Code con guía experta de React 19
Estas recetas de skill están diseñadas para Claude Code, pero también funcionan con otros agentes de codificación con IA que admitan archivos de skill/instrucciones.
Receta
El contenido completo de SKILL.md que puedes copiar en .claude/skills/react-19-mastery/SKILL.md:
---
name: react-19-mastery
description: "Guía experta sobre las funciones de React 19, el compilador, las actions y los nuevos hooks. Úsala cuando te pidan: ayuda con React 19, nuevos hooks, server components, React Compiler, useActionState, useOptimistic, hook use(), migrar desde React 18."
allowed-tools: "Read, Write, Edit, Glob, Grep, Bash(npm:*), Bash(npx:*), Agent"
---
# Dominio de React 19
Eres un experto en React 19. Cuando se invoque esta skill, proporciona orientación autorizada sobre las funciones de React 19, la migración y las mejores prácticas.
## Referencia de funciones principales de React 19
### Nuevos hooks
1. **useActionState(action, initialState, permalink?)** — Gestiona el state de formularios/actions con seguimiento de pending
- Devuelve [state, formAction, isPending]
- Reemplaza useFormState (renombrado en la versión estable de React 19)
- Úsalo para: cualquier envío de formulario, integración con server actions, mejora progresiva
- La action recibe previousState como primer argumento
2. **useOptimistic(state, updateFn)** — Feedback instantáneo en la UI durante operaciones asíncronas
- Devuelve [optimisticState, addOptimistic]
- Revierte automáticamente cuando el re-renderizado del padre termina
- Úsalo para: likes, marcadores, toggles, cualquier mutación cuyo resultado puedas predecir
3. **useFormStatus()** — Lee el estado de envío del formulario padre desde un componente hijo
- Devuelve { pending, data, method, action }
- DEBE llamarse desde un componente renderizado dentro de un <form>
- Úsalo para: estados deshabilitados del botón de envío, spinners de carga dentro de formularios
4. **use(resource)** — Desenvuelve promises y context de forma condicional
- Funciona con Promises (compatible con Suspense) y Context
- PUEDE llamarse dentro de condicionales y bucles (a diferencia de otros hooks)
- Úsalo para: leer datos asíncronos en el render, lecturas condicionales de context
### React Compiler
- Memoriza automáticamente componentes, hooks y expresiones
- Elimina la mayoría del uso manual de useMemo, useCallback y React.memo
- Las reglas de React deben seguirse estrictamente (sin mutaciones durante el render, orden estable de llamadas a hooks)
- Instalación: babel-plugin-react-compiler o el plugin de Vite/Next.js
- Valida con eslint-plugin-react-compiler antes de habilitarlo
### Server Components (RSC)
- Por defecto en App Router (sin directiva "use client" = Server Component)
- Pueden acceder directamente a bases de datos, sistemas de archivos y APIs solo de servidor
- No pueden usar hooks, manejadores de eventos, APIs del navegador ni state
- Los props pasados a Client Components deben ser serializables
### Server Actions
- Funciones async marcadas con "use server" al inicio del cuerpo de la función o del archivo
- Pueden pasarse a formularios como action={serverAction}
- Pueden llamarse desde Client Components como funciones async normales
- Valida siempre las entradas en el servidor aunque exista validación en el cliente
### ref como prop
- Los componentes de función ahora aceptan ref como un prop regular
- forwardRef ya no es necesario (sigue funcionando pero está obsoleto)
- Patrón: function Input({ ref, ...props }) { return <input ref={ref} {...props} /> }
### Elevación de metadatos del documento
- Las etiquetas <title>, <meta> y <link> en componentes se elevan automáticamente a <head>
- Funciona tanto en Server como en Client Components
- Elimina la necesidad de next/head o react-helmet en muchos casos
## Reglas de decisión
Cuando un usuario pregunte sobre React 19, sigue estas reglas:
### Cuándo recomendar useActionState
- El usuario está construyendo un formulario que envía datos
- El usuario necesita rastrear el estado pending durante el envío
- El usuario quiere mejora progresiva (funciona sin JS)
- El usuario está migrando desde useFormState
### Cuándo recomendar useOptimistic
- El usuario quiere feedback instantáneo en una mutación
- El resultado optimista es predecible a partir de la entrada
- El usuario está construyendo funciones de like/marcador/toggle
- SIEMPRE combínalo con manejo de errores para revertir en caso de fallo
### Cuándo recomendar use()
- El usuario necesita leer una promise en un componente (envuelve el padre en Suspense)
- El usuario necesita acceso condicional a context
- NO lo recomiendes para la obtención inicial de datos en Next.js (usa Server Components async en su lugar)
### Cuándo recomendar React Compiler
- El usuario está en React 19 y tiene una base de código limpia que sigue las reglas de React
- El usuario tiene problemas de rendimiento por falta de memorización
- SIEMPRE recomienda ejecutar eslint-plugin-react-compiler primero
- NO lo recomiendes si la base de código tiene patrones de mutación o efectos secundarios en el render
## Patrones de migración desde React 18
### useFormState a useActionState
```tsx
// React 18 (react-dom)
import \{ useFormState \} from "react-dom";
const [state, formAction] = useFormState(action, initial);
// React 19 (react)
import \{ useActionState \} from "react";
const [state, formAction, isPending] = useActionState(action, initial);
// Nota: isPending ya viene integrado, no hace falta useFormStatus solo para pendingforwardRef a prop ref
// React 18
const Input = forwardRef<HTMLInputElement, Props>((props, ref) => (
<input ref=\{ref\} \{...props\} />
));
// React 19
function Input(\{ ref, ...props \}: Props & \{ ref?: React.Ref<HTMLInputElement> \}) \{
return <input ref=\{ref\} \{...props\} />;
\}Memorización manual a React Compiler
// React 18 - memorización manual
const MemoComponent = React.memo((\{ data \}: Props) => \{
const processed = useMemo(() => expensive(data), [data]);
const handler = useCallback(() => doThing(data), [data]);
return <Child data=\{processed\} onClick=\{handler\} />;
\});
// React 19 con Compiler: escribe código simple
function Component(\{ data \}: Props) \{
const processed = expensive(data);
const handler = () => doThing(data);
return <Child data=\{processed\} onClick=\{handler\} />;
\}Errores comunes
- Usar useFormStatus fuera de un formulario — Lee el estado del elemento
formpadre más cercano, no de useActionState - Pasar props no serializables a Client Components — Las funciones, clases y Dates no pueden cruzar el límite servidor/cliente
- Usar use() sin Suspense — Las promises pasadas a use() requieren un límite Suspense por encima
- Mutar state en el render con Compiler habilitado — El compilador asume las reglas de React; las mutaciones rompen la memorización
- Llamar Server Actions durante el render — Son async y deben llamarse desde manejadores de eventos o form actions
- Olvidar la directiva "use server" — Debe estar al inicio del cuerpo de la función o del archivo, no solo en un export
## Ejemplo funcional
### Ejemplo 1: El usuario pregunta «Ayúdame a construir un formulario con React 19»
**Prompt del usuario:** «Necesito construir un formulario de comentarios que muestre actualizaciones optimistas»
**La respuesta guiada por la skill incluiría:**
```tsx
// actions.ts
"use server";
import { z } from "zod";
const CommentSchema = z.object({
text: z.string().min(1).max(500),
});
export async function addComment(prevState: any, formData: FormData) {
const parsed = CommentSchema.safeParse({
text: formData.get("text"),
});
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
await db.comment.create({ data: { text: parsed.data.text } });
return { error: null };
}
// CommentForm.tsx
"use client";
import { useActionState, useOptimistic } from "react";
import { addComment } from "./actions";
import { SubmitButton } from "./SubmitButton";
type Comment = { id: string; text: string };
export function CommentForm({ comments }: { comments: Comment[] }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(state, newText: string) => [
...state,
{ id: crypto.randomUUID(), text: newText },
]
);
const [state, formAction, isPending] = useActionState(
async (prev: any, formData: FormData) => {
const text = formData.get("text") as string;
addOptimisticComment(text);
return addComment(prev, formData);
},
{ error: null }
);
return (
<>
<ul>
{optimisticComments.map((c) => (
<li key={c.id}>{c.text}</li>
))}
</ul>
<form action={formAction}>
<input name="text" required />
{state.error && <p className="text-red-500">{state.error.text}</p>}
<SubmitButton />
</form>
</>
);
}// SubmitButton.tsx
"use client";
import { useFormStatus } from "react-dom";
export function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Publicando..." : "Publicar comentario"}
</button>
);
}Ejemplo 2: El usuario pregunta «¿Debería usar React Compiler?»
La respuesta guiada por la skill incluiría:
- Ejecutar
npx eslint-plugin-react-compileren la base de código primero - Buscar patrones de mutación en funciones de render
- Recomendar habilitarlo primero en una ruta/página, no en toda la app
- Mostrar el antes/después de eliminar la memorización manual
Profundización
Cómo funciona la skill
Esta skill convierte a Claude en un especialista en React 19 al proporcionar:
- Referencia de funciones — Firmas de API completas y reglas de uso para cada nueva función de React 19
- Reglas de decisión — Lógica de cuándo usar cada cosa que evita recomendar la función incorrecta para un escenario dado
- Patrones de migración — Código React 18 vs 19 lado a lado para las actualizaciones más comunes
- Detección de anti-patrones — Una lista de errores comunes para que Claude pueda advertir de forma proactiva
Personalización
Puedes extender esta skill:
- Añadiendo las convenciones específicas de tu proyecto (p. ej., «usa siempre nuestro wrapper
useServerAction») - Restringiendo recomendaciones (p. ej., «no recomendar React Compiler hasta que completemos la auditoría»)
- Añadiendo patrones de migración para tus patrones específicos de React 18 (p. ej., hooks de formulario personalizados)
Cómo instalar
Crea el archivo .claude/skills/react-19-mastery/SKILL.md en la raíz de tu proyecto y pega el contenido completo de la sección Receta anterior.
mkdir -p .claude/skills/react-19-mastery
# Luego pega el contenido de Receta en .claude/skills/react-19-mastery/SKILL.mdErrores comunes
- useActionState fue renombrado desde useFormState entre la RC y la versión estable. Muchos posts y tutoriales siguen usando el nombre antiguo. La skill tiene esto en cuenta.
- React Compiler es opt-in y se distribuye como paquete separado. No se habilita automáticamente al actualizar a React 19.
- use() no es useEffect — no ejecuta efectos secundarios. Desenvuelve recursos durante el render.
- Los Server Components no pueden importarse en Client Components — el límite es unidireccional. Los Client Components pueden recibir Server Components como hijos mediante props.
Alternativas
| Enfoque | Cuándo usarlo |
|---|---|
| React 18 + optimización manual | Proyectos legacy que no están listos para migrar |
| Preact Signals | Bundle más pequeño, modelo de reactividad distinto |
| Solid.js | Reactividad granular sin DOM virtual |
| Svelte 5 | Enfoque compiler-first con runes |
Preguntas frecuentes
¿Cuáles son los cuatro nuevos hooks introducidos en React 19?
useActionState(action, initialState)— gestiona el state de formularios/actions con seguimiento de pendinguseOptimistic(state, updateFn)— feedback instantáneo en la UI durante operaciones asíncronasuseFormStatus()— lee el estado de envío del formulario padre desde un componente hijouse(resource)— desenvuelve promises y context, puede llamarse dentro de condicionales
¿En qué se diferencia useActionState del antiguo useFormState?
// React 18 (desde react-dom)
const [state, formAction] = useFormState(action, initial);
// React 19 (desde react)
const [state, formAction, isPending] = useActionState(action, initial);- Renombrado de
useFormStateauseActionState - Movido de
react-domareact - Ahora devuelve
isPendingcomo tercer valor (no hace faltauseFormStatusseparado solo para pending)
¿Qué hace React Compiler y cuándo deberías habilitarlo?
- Memoriza automáticamente componentes, hooks y expresiones
- Elimina la mayoría del uso manual de
useMemo,useCallbackyReact.memo - Solo habilítalo después de ejecutar
eslint-plugin-react-compilerpara comprobar violaciones - No lo habilites si la base de código tiene patrones de mutación o efectos secundarios en el render
¿Cómo funciona ref como prop en React 19 y qué reemplaza?
// React 19 — ref es un prop regular
function Input({ ref, ...props }: Props & { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />;
}forwardRefya no es necesario (sigue funcionando pero está obsoleto)- Los componentes de función aceptan
refdirectamente como prop
Error común: ¿Qué ocurre si usas useFormStatus fuera de un formulario?
- Lee el estado del elemento
<form>padre más cercano - Si no hay formulario padre, no detectará ningún estado de envío
- Renderiza siempre el componente que usa
useFormStatusdentro de un<form>, no en el mismo componente que crea el formulario
¿Cuándo deberías usar useOptimistic y qué ocurre si falla?
- Úsalo cuando puedas predecir el resultado de una mutación (likes, marcadores, toggles)
- Proporciona feedback instantáneo en la UI antes de que termine la operación asíncrona
- Si la operación falla, el state optimista revierte automáticamente cuando el padre re-renderiza
- Combínalo siempre con manejo de errores para revertir en caso de fallo
¿Cuáles son las reglas para Server Components en App Router?
- Por defecto en App Router (sin directiva
"use client"significa Server Component) - Pueden acceder directamente a bases de datos, sistemas de archivos y APIs solo de servidor
- No pueden usar hooks, manejadores de eventos, APIs del navegador ni state
- Los props pasados a Client Components deben ser serializables
Error común: ¿Puedes pasar props no serializables de un Server Component a un Client Component?
- No. Las funciones, clases y objetos Date no pueden cruzar el límite servidor/cliente
- Solo datos serializables (strings, numbers, objetos planos, arrays) pueden pasarse como props
- Convierte valores no serializables a formatos serializables antes de pasarlos
¿Cómo tipar useActionState con TypeScript para una form action?
type FormState = { error: string | null; success: boolean };
const [state, formAction, isPending] = useActionState(
async (prev: FormState, formData: FormData): Promise<FormState> => {
// ...
return { error: null, success: true };
},
{ error: null, success: false }
);- La action recibe
previousStatecomo primer argumento yFormDatacomo segundo - El tipo de retorno debe coincidir con el tipo del state inicial
¿En qué se diferencia el hook use() de useEffect?
use()desenvuelve recursos (Promises, Context) durante el render, no como efecto secundario- Requiere un límite
<Suspense>por encima cuando se usa con Promises - A diferencia de otros hooks, PUEDE llamarse dentro de condicionales y bucles
- No lo uses para la obtención inicial de datos en Next.js — usa Server Components async en su lugar
¿Cuál es la ruta de migración desde React.memo y useMemo hacia React Compiler?
// React 18 — memorización manual
const MemoComponent = React.memo(({ data }: Props) => {
const processed = useMemo(() => expensive(data), [data]);
return <Child data={processed} />;
});
// React 19 con Compiler — código simple
function Component({ data }: Props) {
const processed = expensive(data);
return <Child data={processed} />;
}- El Compiler gestiona la memorización automáticamente
- Elimina los wrappers de
React.memo,useMemoyuseCallback
¿Qué significa la elevación de metadatos del documento en React 19?
- Las etiquetas
<title>,<meta>y<link>en componentes se elevan automáticamente a<head> - Funciona tanto en Server como en Client Components
- Elimina la necesidad de
next/headoreact-helmeten muchos casos