Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// Paso 1: Instala el compilador
// npm install -D babel-plugin-react-compiler
// Paso 2: Añade a tu configuración de Next.js (next.config.ts)
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;
// Paso 3: Elimina la memorización manual - el compilador se encarga
// ANTES: Memorización manual
function ProductList({ products, category }: Props) {
const filtered = useMemo(
() => products.filter((p) => p.category === category),
[products, category]
);
const handleClick = useCallback((id: string) => {
selectProduct(id);
}, [selectProduct]);
return <MemoizedGrid items={filtered} onClick={handleClick} />;
}
const MemoizedGrid = memo(Grid);
// DESPUÉS: El compilador auto-memoriza - código más limpio, mismo rendimiento
function ProductList({ products, category }: Props) {
const filtered = products.filter((p) => p.category === category);
const handleClick = (id: string) => selectProduct(id);
return <Grid items={filtered} onClick={handleClick} />;
}Cuándo usarlo: Al iniciar un nuevo proyecto React 19+ o migrar uno existente. El compilador elimina el código boilerplate de useMemo, useCallback y memo manteniendo un rendimiento equivalente o mejor.
// ---- ANTES: Memorización manual con 15 líneas de boilerplate ----
import { memo, useMemo, useCallback, useState } from "react";
interface User {
id: string;
name: string;
role: "admin" | "user";
lastActive: Date;
}
function UserDashboard({ users }: { users: User[] }) {
const [search, setSearch] = useState("");
const [roleFilter, setRoleFilter] = useState<"all" | "admin" | "user">("all");
const [selectedId, setSelectedId] = useState<string | null>(null);
const filteredUsers = useMemo(
() =>
users
.filter((u) => roleFilter === "all" || u.role === roleFilter)
.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => b.lastActive.getTime() - a.lastActive.getTime()),
[users, roleFilter, search]
);
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);
const stats = useMemo(
() => ({
total: users.length,
admins: users.filter((u) => u.role === "admin").length,
active: users.filter((u) => {
const hourAgo = Date.now() - 3600_000;
return u.lastActive.getTime() > hourAgo;
}).length,
}),
[users]
);
return (
<div>
<MemoizedStatsBar stats={stats} />
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<select
value={roleFilter}
onChange={(e) => setRoleFilter(e.target.value as "all" | "admin" | "user")}
>
<option value="all">Todos</option>
<option value="admin">Admin</option>
<option value="user">Usuario</option>
</select>
<MemoizedUserList
users={filteredUsers}
selectedId={selectedId}
onSelect={handleSelect}
/>
</div>
);
}
const MemoizedStatsBar = memo(function StatsBar({
stats,
}: {
stats: { total: number; admins: number; active: number };
}) {
return (
<div className="flex gap-4">
<span>Total: {stats.total}</span>
<span>Admins: {stats.admins}</span>
<span>Activos: {stats.active}</span>
</div>
);
});
const MemoizedUserList = memo(function UserList({
users,
selectedId,
onSelect,
}: {
users: User[];
selectedId: string | null;
onSelect: (id: string) => void;
}) {
return (
<ul>
{users.map((user) => (
<li
key={user.id}
onClick={() => onSelect(user.id)}
className={user.id === selectedId ? "bg-blue-100" : ""}
>
{user.name} ({user.role})
</li>
))}
</ul>
);
});
// ---- DESPUÉS: Compilador de React - mismo rendimiento, cero boilerplate ----
function UserDashboard({ users }: { users: User[] }) {
const [search, setSearch] = useState("");
const [roleFilter, setRoleFilter] = useState<"all" | "admin" | "user">("all");
const [selectedId, setSelectedId] = useState<string | null>(null);
const filteredUsers = users
.filter((u) => roleFilter === "all" || u.role === roleFilter)
.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => b.lastActive.getTime() - a.lastActive.getTime());
const stats = {
total: users.length,
admins: users.filter((u) => u.role === "admin").length,
active: users.filter((u) => {
const hourAgo = Date.now() - 3600_000;
return u.lastActive.getTime() > hourAgo;
}).length,
};
return (
<div>
<StatsBar stats={stats} />
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<select
value={roleFilter}
onChange={(e) => setRoleFilter(e.target.value as "all" | "admin" | "user")}
>
<option value="all">Todos</option>
<option value="admin">Admin</option>
<option value="user">Usuario</option>
</select>
<UserList
users={filteredUsers}
selectedId={selectedId}
onSelect={(id: string) => setSelectedId(id)}
/>
</div>
);
}
function StatsBar({ stats }: { stats: { total: number; admins: number; active: number } }) {
return (
<div className="flex gap-4">
<span>Total: {stats.total}</span>
<span>Admins: {stats.admins}</span>
<span>Activos: {stats.active}</span>
</div>
);
}
function UserList({
users,
selectedId,
onSelect,
}: {
users: User[];
selectedId: string | null;
onSelect: (id: string) => void;
}) {
return (
<ul>
{users.map((user) => (
<li
key={user.id}
onClick={() => onSelect(user.id)}
className={user.id === selectedId ? "bg-blue-100" : ""}
>
{user.name} ({user.role})
</li>
))}
</ul>
);
}Lo que esto demuestra:
useMemo x2, useCallback x1, wrappers de memo x2) y 15 líneas de boilerplateReact.memo.useMemo manual que cachea con la granularidad que especifiques, el compilador puede cachear elementos JSX individuales dentro de un componente, potencialmente logrando una granularidad mejor que la optimización escrita a mano.Exclusión con la directiva "use no memo":
// Excluye un componente específico de la optimización del compilador
function DebugPanel({ data }: { data: unknown }) {
"use no memo";
// Este componente siempre se re-renderiza - útil para depuración
console.log("DebugPanel rendered at", Date.now());
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Inclusión por archivo (migración gradual):
// next.config.ts - solo compila directorios específicos
const nextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation",
// O apunta a rutas específicas:
// include: ["src/components/**"],
},
},
};// Incluye a nivel de componente con "use memo"
function ExpensiveComponent({ data }: Props) {
"use memo";
// El compilador optimiza este componente
return <Chart data={data} />;
}Verificar salida del compilador:
# Ve qué transforma el compilador
npx react-compiler-healthcheck
# O inspecciona la salida compilada
npx babel --plugins babel-plugin-react-compiler src/Component.tsx"use no memo" es un literal de cadena y no necesita una declaración de tipo.El estado mutable externo rompe los supuestos del compilador - El compilador asume renderizado puro. Si un componente lee de una variable global mutable o una ref durante el renderizado, el compilador puede cachear un resultado obsoleto. Solución: Usa useSyncExternalStore para tiendas externas, o añade la directiva "use no memo".
Efectos secundarios durante el renderizado - El código que registra, muta objetos externos o dispara solicitudes de red durante el renderizado será cacheado por el compilador, lo que significa que los efectos secundarios pueden no ejecutarse en cada renderizado como se esperaba. Solución: Mueve efectos secundarios dentro de useEffect o manejadores de eventos.
Computaciones no idempotentes - Si una computación de valor se basa en Date.now(), Math.random() u otras fuentes no deterministas, el compilador puede cachear un resultado obsoleto. Solución: Mueve valores no deterministas dentro del state o effects.
Los componentes de clase no se optimizan - El compilador solo funciona con componentes de función y hooks. Los componentes de clase se pasan sin cambios. Solución: Migra componentes de clase a componentes de función.
La memorización manual sigue siendo necesaria en casos raros - El compilador puede no optimizar límites entre componentes en todos los casos, particularmente con componentes de orden superior o render props. Solución: Perfila después de activar el compilador. Añade memo manual solo donde la perfilación muestre que el compilador perdió una optimización.
Mantén la memorización manual existente durante la migración - Eliminar useMemo/useCallback antes de activar el compilador deja tu aplicación sin optimizar. Solución: Activa el compilador primero, verifica que el rendimiento se mantenga, luego elimina la memorización manual.
| Enfoque | Compensación |
|---|---|
useMemo + useCallback + memo manual | Control total; detallado; arrays de dependencias propensos a errores |
| Compilador de React | Cero boilerplate; requiere React 19+; experimental |
| Colocación de state | Evita la necesidad de memorización en absoluto; puede requerir reestructuración |
| Selectores de Zustand | Suscripciones de grano fino; añade dependencia externa |
| Million.js | Compilador alternativo para optimización de DOM virtual; de terceros |
| Signals (Preact, Solid) | Modelo reactivo evita el problema de re-renderizado; framework diferente |
El Compilador de React es un plugin de Babel que analiza el código del componente en tiempo de compilación e inserta automáticamente memorización (equivalente a useMemo, useCallback y memo). A diferencia de la memorización manual, no escribes arrays de dependencias - el compilador rastrea dependencias estáticamente.
// next.config.ts
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;También instala el plugin de Babel: npm install -D babel-plugin-react-compiler.
Sí. Añade la directiva "use no memo" en la parte superior del cuerpo del componente:
function DebugPanel({ data }: { data: unknown }) {
"use no memo";
console.log("Rendered at", Date.now());
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Violar estas reglas puede producir salida cacheada incorrecta.
El compilador puede cachear un resultado obsoleto porque asume renderizado puro. Si un componente lee de window.someGlobal o una ref mutable durante el renderizado, la salida cacheada no reflejará actualizaciones a esa global.
Solución: Usa useSyncExternalStore para tiendas externas, o añade "use no memo".
El compilador cachea la salida del componente. Si efectos secundarios (logging, solicitudes de red, mutaciones) se ejecutan durante el renderizado, pueden ser saltados cuando la salida cacheada se reutiliza.
Solución: Mueve efectos secundarios dentro de useEffect o manejadores de eventos.
No. El compilador solo optimiza componentes de función y hooks. Los componentes de clase se pasan sin cambios. Migra componentes de clase a componentes de función para beneficiarte.
No. Activa el compilador primero, verifica que el rendimiento se mantenga, luego elimina la memorización manual. Eliminar hooks antes de activar el compilador deja tu aplicación sin optimizar durante la brecha.
No. El compilador funciona con TypeScript de forma inmediata. Las anotaciones de tipo se preservan -- el compilador solo transforma el comportamiento en tiempo de ejecución. Los componentes genéricos se admiten sin cambios.
npx react-compiler-healthcheck
# O inspecciona la salida compilada directamente:
npx babel --plugins babel-plugin-react-compiler src/Component.tsxEn el modo compilationMode: "annotation", solo los componentes con la directiva "use memo" se optimizan por el compilador. Esto permite la migración gradual al optar por componente.
useMemo manual cachea con la granularidad que especifiques (un valor por llamada a hook). El compilador puede cachear elementos JSX individuales dentro de un componente, potencialmente logrando una granularidad mejor que la optimización escrita a mano.
Revisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥