Parte de nuestra serie Performance & Scalability
Leer la guía completaComponentes del servidor React 19: qué cambió y por qué
React 19 es el lanzamiento más importante desde Hooks. Los componentes del servidor, que comenzaron como una característica experimental en React 18, ahora son estables y están completamente integrados con el modelo de renderizado concurrente. Pero los cambios van mucho más allá de simplemente estabilizar RSC: React 19 presenta Acciones, un nuevo gancho use(), integración de formularios, actualizaciones optimistas y gestión de metadatos de documentos que en conjunto cambian la forma en que piensa sobre el flujo de datos en las aplicaciones React.
Esta guía se centra en lo que realmente cambió entre React 18 y 19, explica el razonamiento arquitectónico detrás de cada cambio y muestra los patrones de producción que reemplazan los enfoques anteriores.
Conclusiones clave
- Los componentes del servidor se ejecutan solo en el servidor: no tienen ciclo de vida, estado ni API de navegador.
- El gancho
use()reemplaza aawaiten Componentes del Cliente para consumir promesas y contexto- Las acciones de React 19 reemplazan los patrones manuales
useState+fetchpara envíos de formulariosuseOptimistichabilita actualizaciones instantáneas de la interfaz de usuario antes de la confirmación del servidoruseFormStatusle brinda un estado pendiente sin necesidad de perforar los componentes del formulario.- Las API de precarga de activos (
preload,preinit) le permiten controlar la carga de recursos desde los componentes- Las etiquetas
<title>,<meta>y<link>en los componentes se elevan automáticamente a<head>- Los componentes del servidor y los componentes del cliente forman un árbol: los componentes del cliente no pueden importar componentes del servidor
¿Qué son realmente los componentes del servidor?
Antes de discutir qué cambió, aclare qué son los componentes del servidor: componentes de React que se procesan exclusivamente en el servidor y producen una carga útil HTML + RSC que el cliente hidrata. No son lo mismo que los componentes de cliente renderizados en el lado del servidor.
Las diferencias clave:
| Componentes del servidor | Componentes del cliente | |
|---|---|---|
| Funciona en | Sólo servidor | Servidor (renderizado inicial) + Cliente |
| Puede utilizar ganchos | No | Sí |
| Puede utilizar las API del navegador | No | Sí |
| Puede acceder a la base de datos | Sí (directamente) | No (a través de API) |
| Impacto del tamaño del paquete | Cero | Sí |
| Puede ser asíncrono | Sí | No (sin suspenso) |
// Server Component — async, no hooks, direct DB access
async function UserProfile({ userId }: { userId: string }) {
// Direct database query — no API needed
const user = await db.query.users.findFirst({
where: eq(users.id, userId),
});
return (
<div>
<h1>{user.name}</h1>
{/* Client Component gets data as props */}
<UserActions userId={user.id} role={user.role} />
</div>
);
}
// Client Component — can use hooks, handles interactions
'use client';
function UserActions({ userId, role }: { userId: string; role: string }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
<button onClick={() => setIsEditing(true)}>Edit</button>
{isEditing && <EditUserForm userId={userId} />}
</div>
);
}
Reaccionar 19 Acciones
La mayor mejora ergonómica en React 19 son las Acciones, una forma estandarizada de manejar operaciones asíncronas desencadenadas por las interacciones del usuario, particularmente el envío de formularios.
Antes de React 19, el manejo de formularios era un texto repetitivo:
// React 18 — manual state management
function ContactForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setSubmitting(true);
setError(null);
try {
await createContact({ name, email });
} catch (err) {
setError(err.message);
} finally {
setSubmitting(false);
}
}
return (
<form onSubmit={handleSubmit}>
{/* ... */}
</form>
);
}
Las acciones de React 19 simplifican esto significativamente:
// React 19 — useActionState
'use client';
import { useActionState } from 'react';
async function createContactAction(
prevState: { error?: string },
formData: FormData
) {
'use server'; // Server Action — runs on the server
const name = formData.get('name') as string;
const email = formData.get('email') as string;
try {
await createContact({ name, email });
return { success: true };
} catch (err) {
return { error: err.message };
}
}
function ContactForm() {
const [state, formAction, isPending] = useActionState(
createContactAction,
{}
);
return (
<form action={formAction}>
<input name="name" required />
<input name="email" type="email" required />
{state.error && <p className="text-red-500">{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
El gancho useActionState gestiona el estado pendiente, los datos del formulario y el manejo de errores en un solo lugar. La directiva 'use server' en la función de acción la marca como una Acción del Servidor: se ejecuta en el servidor, puede acceder a la base de datos directamente y el cliente nunca ve la implementación.
Acciones del servidor
Las acciones del servidor son funciones asíncronas con la directiva 'use server' que se ejecutan en el servidor cuando se llaman desde el cliente. Reemplazan las rutas API para la mayoría de los casos de uso de mutación de datos:
// app/actions/contacts.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { db } from '@ecosire/db';
import { contacts } from '@ecosire/db/schema';
export async function createContact(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
if (!name || !email) {
throw new Error('Name and email are required');
}
await db.insert(contacts).values({
name,
email,
organizationId: await getOrganizationId(), // From session
});
revalidatePath('/dashboard/contacts'); // Invalidate cached page
redirect('/dashboard/contacts'); // Redirect after success
}
Las acciones del servidor se pueden utilizar directamente en elementos form:
import { createContact } from '@/app/actions/contacts';
export default function NewContactPage() {
return (
<form action={createContact}>
<input name="name" placeholder="Name" required />
<input name="email" placeholder="Email" type="email" required />
<button type="submit">Create Contact</button>
</form>
);
}
Sin controlador onSubmit, sin preventDefault(), sin manual fetch(): el formulario simplemente funciona.
El gancho use()
React 19 introduce el gancho use() para consumir recursos (Promises y Context) dentro del render. A diferencia de await, funciona con el sistema Suspense de React:
// Before React 19 — had to resolve at the top level
export default async function Page() {
const posts = await getPosts(); // Blocks entire page
return <PostList posts={posts} />;
}
// React 19 — pass a promise, resolve with use()
export default function Page() {
const postsPromise = getPosts(); // Start fetch immediately
return (
<Suspense fallback={<PostsSkeleton />}>
<PostList postsPromise={postsPromise} />
</Suspense>
);
}
// Client Component using use() to consume the promise
'use client';
function PostList({ postsPromise }: { postsPromise: Promise<Post[]> }) {
const posts = use(postsPromise); // Suspends until resolved
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
El gancho use() también reemplaza a useContext(): puede consumir contexto condicionalmente (a diferencia de los ganchos, use() se puede llamar dentro de condiciones y bucles):
'use client';
import { use } from 'react';
import { ThemeContext } from '@/contexts/theme';
function Button({ children }: { children: React.ReactNode }) {
const theme = use(ThemeContext); // Can be inside conditions
return (
<button className={theme === 'dark' ? 'bg-gray-800' : 'bg-white'}>
{children}
</button>
);
}
Actualizaciones optimistas con useOptimistic
useOptimistic proporciona información instantánea de la interfaz de usuario antes de que se complete una operación del servidor y se revierte automáticamente si la operación falla:
'use client';
import { useOptimistic, useTransition } from 'react';
interface Like {
id: string;
count: number;
userLiked: boolean;
}
function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: Like }) {
const [likes, setOptimisticLikes] = useOptimistic(
initialLikes,
(state, action: 'like' | 'unlike') => ({
...state,
count: action === 'like' ? state.count + 1 : state.count - 1,
userLiked: action === 'like',
})
);
const [isPending, startTransition] = useTransition();
async function toggleLike() {
const action = likes.userLiked ? 'unlike' : 'like';
startTransition(async () => {
setOptimisticLikes(action); // Instant UI update
// Server operation — if this fails, optimistic state reverts
await togglePostLike(postId, action);
});
}
return (
<button onClick={toggleLike} disabled={isPending}>
{likes.userLiked ? '♥' : '♡'} {likes.count}
</button>
);
}
La actualización optimista se muestra inmediatamente y luego se confirma (éxito del servidor) o se revierte (fallo del servidor). Los usuarios obtienen comentarios instantáneos sin tener que esperar los viajes de ida y vuelta de la red.
Metadatos del documento de componentes
React 19 permite que las etiquetas <title>, <meta> y <link> se representen dentro de cualquier componente; se elevan automáticamente al documento <head>:
// Server Component — metadata hoists to <head> automatically
async function BlogPost({ slug }: { slug: string }) {
const post = await getPost(slug);
return (
<article>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<link rel="canonical" href={`https://ecosire.com/blog/${slug}`} />
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}
Esto funciona tanto en componentes de servidor como de cliente. En marcos como Next.js, seguirás usando generateMetadata() para tener control total sobre las etiquetas OpenGraph y los atributos hreflang; el soporte nativo de React 19 es más básico. Pero para casos simples, elimina la necesidad de next/head o bibliotecas similares.
API de carga de activos
React 19 proporciona API explícitas para precargar recursos, lo que le brinda un control detallado sobre la cascada de carga de recursos:
import { preload, preinit, prefetchDNS, preconnect } from 'react-dom';
function BelowFoldSection() {
// When this component renders, preload the hero image for next section
preload('/images/hero-next.webp', { as: 'image' });
// Preinit a script (loads and executes immediately)
preinit('https://cdn.example.com/analytics.js', { as: 'script' });
// DNS prefetch for external resources
prefetchDNS('https://fonts.googleapis.com');
// Establish connection early
preconnect('https://api.ecosire.com');
return <section>{/* content */}</section>;
}
Estas API funcionan correctamente con el modelo de renderizado de React: agrupan sugerencias de recursos y las emiten en el punto óptimo del documento, incluso desde lo más profundo del árbol de componentes.
Errores y soluciones comunes
Error 1: intentar utilizar enlaces en componentes del servidor
Los componentes del servidor no pueden usar useState, useEffect, useContext ni ningún otro enlace. Si los necesita, agregue 'use client' al componente. El error suele ser: Error: Hooks can only be called inside a function component.
Error 2: Importar componentes de servidor desde componentes de cliente
Los componentes del cliente no pueden importar componentes del servidor (lo contrario está bien). Esto se debe a que los componentes del cliente se ejecutan en el navegador donde los componentes del servidor no existen. Si necesita componerlos, pase los componentes del servidor como accesorios children:
// Wrong — Client Component cannot import Server Component
'use client';
import { ServerUserProfile } from './server-user-profile'; // Error
// Correct — pass as children from a Server Component parent
// Parent (Server):
<ClientShell>
<ServerUserProfile userId={userId} />
</ClientShell>
// ClientShell:
'use client';
function ClientShell({ children }: { children: React.ReactNode }) {
return <div className="shell">{children}</div>;
}
Error 3: errores de serialización al pasar accesorios del servidor al cliente
Solo los datos serializables en JSON pueden cruzar el límite Servidor-Cliente como accesorios. Las funciones, instancias de clases, mapas, conjuntos y fechas (como objetos) no se pueden serializar. Convertir fechas a cadenas ISO; reemplace funciones con identificadores de cadena.
Error 4: las acciones del servidor no validan la entrada
Nunca confíes en los datos proporcionados por el cliente en Server Actions. Validar siempre con Zod o similar antes de escribir en la base de datos:
'use server';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2).max(255),
email: z.string().email(),
});
export async function createContact(formData: FormData) {
const result = schema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!result.success) {
return { error: result.error.flatten().fieldErrors };
}
await db.insert(contacts).values(result.data);
}
Preguntas frecuentes
¿Los componentes del servidor están disponibles fuera de Next.js?
Los componentes del servidor React son una característica de React, pero requieren un marco para manejar la infraestructura de renderizado del servidor: enrutamiento, agrupación y transmisión. Next.js App Router es la implementación más madura. Las configuraciones basadas en Remix, Astro y Vite agregan soporte RSC. El uso de RSC sin un marco requiere un importante trabajo de infraestructura personalizada.
¿Cómo se comparan las acciones del servidor con las rutas de API REST?
Las acciones del servidor son más simples para las mutaciones ubicadas con la interfaz de usuario: no hay URL de punto final que administrar, no hay llamada fetch() para escribir, no hay texto estándar para el manejo de errores. Las rutas de API REST son mejores para operaciones llamadas desde fuera de la aplicación web (aplicaciones móviles, webhooks, integraciones de terceros), para API públicas que necesitan documentación y cuando necesita códigos de estado HTTP explícitos. Utilice ambos en la misma aplicación según el caso de uso.
¿Cuál es el impacto en el rendimiento de los componentes del servidor?
Los componentes del servidor reducen el tamaño del paquete de JavaScript (el código del componente nunca se envía al navegador), eliminan las cascadas de obtención de datos del lado del cliente y permiten la entrega de HTML a través de Suspense. La desventaja es el costo de computación del servidor: la representación ocurre en sus servidores, no en el dispositivo del cliente. Para páginas con muchos datos, esto casi siempre es una ganancia neta.
¿Puedo mezclar React 18 y React 19 en una base de código?
Todo el código de React se ejecuta como React 19: no hay control de versiones por archivo. La pregunta es si su código React 18 existente funciona en React 19. La mayoría del código React 18 funciona sin cambios. Los principales cambios importantes se refieren a las referencias (ahora un accesorio normal), la eliminación de ReactDOM.render y algunos cambios tipográficos en @types/react. Ejecute los codemods de React 19 para una migración automatizada.
¿Cómo pruebo los componentes del servidor?
Los componentes del servidor se pueden probar con la biblioteca de pruebas React usando async render. Para las acciones del servidor, pruébelas como funciones asíncronas simples con llamadas simuladas a la base de datos. Las pruebas de un extremo a otro con Playwright cubren la integración completa del componente del servidor + componente del cliente sin ninguna configuración especial: prueban el resultado HTML final.
Próximos pasos
Los componentes del servidor React 19 representan un cambio fundamental en la forma en que se crean las aplicaciones web modernas. El equipo de frontend de ECOSIRE ha creado aplicaciones de producción en esta arquitectura: 249 páginas con componentes de servidor, acciones de servidor y actualizaciones optimistas que impulsan flujos de trabajo de usuarios reales.
Si necesita ayuda para migrar de React 18 a 19, diseñar una nueva aplicación RSC o simplemente quiere un ingeniero frontend experto en su equipo, explore nuestros servicios de desarrollo.
Escrito por
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Haga crecer su negocio con ECOSIRE
Soluciones empresariales en ERP, comercio electrónico, inteligencia artificial, análisis y automatización.
Artículos relacionados
Guía de migración de componentes de servidor React 19 2026: patrones de producción reales
Guía de migración de componentes de servidor React 19 probada en batalla: obtención de datos, transmisión, trampas de suspenso, límites cliente/servidor, dificultades y ganancias de rendimiento medidas.
Prueba de carga de k6: pruebe sus API antes del lanzamiento
Domine las pruebas de carga de k6 para las API de Node.js. Cubre aumentos de usuarios virtuales, umbrales, escenarios, HTTP/2, pruebas de WebSocket, paneles de Grafana y patrones de integración de CI.
Next.js 16 App Router: patrones de producción y dificultades
Patrones de enrutador de aplicaciones Next.js 16 listos para producción: componentes del servidor, estrategias de almacenamiento en caché, API de metadatos, límites de errores y problemas de rendimiento que se deben evitar.
Más de Performance & Scalability
Depuración y monitoreo de Webhook: la guía completa de solución de problemas
Domine la depuración de webhooks con esta guía completa que cubre patrones de falla, herramientas de depuración, estrategias de reintento, paneles de monitoreo y mejores prácticas de seguridad.
Prueba de carga de k6: pruebe sus API antes del lanzamiento
Domine las pruebas de carga de k6 para las API de Node.js. Cubre aumentos de usuarios virtuales, umbrales, escenarios, HTTP/2, pruebas de WebSocket, paneles de Grafana y patrones de integración de CI.
Configuración de producción de Nginx: SSL, almacenamiento en caché y seguridad
Guía de configuración de producción de Nginx: terminación SSL, HTTP/2, encabezados de almacenamiento en caché, encabezados de seguridad, limitación de velocidad, configuración de proxy inverso y patrones de integración de Cloudflare.
Ajuste del rendimiento de Odoo: PostgreSQL y optimización del servidor
Guía experta para ajustar el rendimiento de Odoo 19. Cubre la configuración, indexación, optimización de consultas, almacenamiento en caché de Nginx y dimensionamiento del servidor de PostgreSQL para implementaciones empresariales.
Odoo vs Acumatica: ERP en la nube para empresas en crecimiento
Comparación de Odoo y Acumatica para 2026: modelos de precios únicos, escalabilidad, profundidad de fabricación y qué ERP en la nube se adapta a su trayectoria de crecimiento.
Prueba y seguimiento de agentes de IA en producción
Una guía completa para probar y monitorear agentes de IA en entornos de producción. Cubre marcos de evaluación, observabilidad, detección de deriva y respuesta a incidentes para implementaciones de OpenClaw.