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 Research and Development Team
Construyendo productos digitales de nivel empresarial en ECOSIRE. Compartiendo perspectivas sobre integraciones Odoo, automatización de eCommerce y soluciones empresariales impulsadas por IA.
Artículos relacionados
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Next.js 16 App Router: Production Patterns and Pitfalls
Production-ready Next.js 16 App Router patterns: server components, caching strategies, metadata API, error boundaries, and performance pitfalls to avoid.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Más de Performance & Scalability
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Nginx Production Configuration: SSL, Caching, and Security
Nginx production configuration guide: SSL termination, HTTP/2, caching headers, security headers, rate limiting, reverse proxy setup, and Cloudflare integration patterns.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Odoo vs Acumatica: Cloud ERP for Growing Businesses
Odoo vs Acumatica compared for 2026: unique pricing models, scalability, manufacturing depth, and which cloud ERP fits your growth trajectory.
Testing and Monitoring AI Agents in Production
A complete guide to testing and monitoring AI agents in production environments. Covers evaluation frameworks, observability, drift detection, and incident response for OpenClaw deployments.
Compliance Monitoring Agents with OpenClaw
Deploy OpenClaw AI agents for continuous compliance monitoring. Automate regulatory checks, policy enforcement, audit trail generation, and compliance reporting.