A principios de 2023, un cliente mío, una startup fintech basada en Canary Wharf, me pasó una base de código Next.js 13 que una agencia anterior había construido. Estaba usando el nuevo directorio app/. Bien. Excepto que los errores de hidratación estaban por todas partes, el bundle tenía 340 KB comprimido, y nadie del equipo anterior podía explicar por qué habían puesto "use client" en literalmente cada archivo. Cuando les pregunté sobre React Server Components, dijeron: "Oh sí, usamos esos". No usaban esos.
Ese es el problema con los RSCs en este momento. Todos afirman entenderlos. Casi nadie realmente lo hace. Así que déjame darte la versión que hubiera deseado que existiera a principios de 2023.
Lo Que Los RSCs Realmente Son (No la Versión de Marketing)
React Server Components son componentes que se ejecutan solo en el servidor y nunca envían su JavaScript al navegador. Punto final.
No "renderización del lado del servidor". No "pre-renderización". Esas cosas existían antes de los RSCs y funcionan diferente. Con SSR tradicional (lo que hace el pages router de Next.js), tus componentes se renderizan en el servidor para producir HTML, pero luego el mismo JavaScript se envía al cliente para que React pueda "hidratar" la página, adjuntar event listeners y tomar el control.
Los RSCs omiten completamente la segunda parte. El componente se ejecuta en el servidor, se renderiza, envía su salida como un payload serializado al cliente, y luego... eso es todo. Nunca llega JavaScript para ese componente al navegador.
El efecto práctico: si tienes un componente <ProductDescription> que usa un parser de markdown de 40 KB para renderizar texto, y lo conviertes en un componente de servidor, esos 40 KB nunca llegan al usuario. El HTML parseado lo hace. Eso es todo.
El RFC original del equipo de React realmente vale la pena leer si quieres conocer toda la fundamentación. Es denso pero honesto.
El Modelo Mental Que Finalmente Me Hizo Entenderlo
Piensa en tu árbol de componentes como dos mundos separados que están cosidos juntos.
Mundo 1 (Servidor). Tiene acceso completo a tu base de datos, tu sistema de archivos, tus variables de entorno, tus secretos. No puede usar useState, useEffect, ni ninguna API del navegador. No puede adjuntar escuchadores de eventos.
Mundo 2 (Cliente). Se ejecuta en el navegador. Puede usar todos los hooks de React que conoces. No puede comunicarse directamente con tu base de datos. Envía solicitudes a APIs en su lugar.
Antes de RSCs, cada componente vivía en el Mundo 2, incluso si se renderizaba primero en el servidor. Con RSCs, ahora puedes colocar explícitamente componentes en el Mundo 1. Esos componentes pueden renderizar componentes del Mundo 2 como hijos, pasándoles props serializables. Pero los componentes del Mundo 2 no pueden renderizar componentes del Mundo 1. El límite es unidireccional.
Aquí es donde la gente se confunde: no añades una directiva para hacer algo un componente de servidor. En el directorio app/ de Next.js, todo es un componente de servidor por defecto. Optas por el cliente con "use client". Al revés de lo que la mayoría asume.
Seahawk tuvo un proyecto el año pasado, un gran catálogo de comercio electrónico para un minorista del Reino Unido, donde auditamos unos 60 componentes. Resultó que aproximadamente 40 de ellos tenían "use client" sin razón. Eliminarlo redujo el bundle de JavaScript en aproximadamente 28%. Una tarde de trabajo.
Lo Que Puedes y No Puedes Hacer (Un Desglose Concreto)
Los Componentes de Servidor pueden:
- Obtener datos directamente con async/await, sin useEffect, sin estados de carga, solo
const data = await db.query(...) - Importar librerías pesadas solo para servidor (como gray-matter para análisis de encabezados, o generadores de PDF) sin tocar el bundle del cliente
- Leer del sistema de archivos usando el módulo
fsde Node - Acceder a variables de entorno que no quieres exponer al navegador
- Pasar datos a componentes cliente como props
Los Componentes de Servidor no pueden:
- Usar
useStateouseReducer - Usar
useEffectouseLayoutEffect - Adjuntar manejadores de eventos (no onClick, no
onChange) - Usar APIs del navegador (window, document,
localStorage) - Usar React Context directamente (aunque hay patrones para trabajar alrededor de esto)
Los Componentes Cliente pueden hacer todo lo anterior que los Componentes de Servidor no pueden, pero:
- No pueden llamar directamente a tu base de datos
- No pueden usar paquetes solo para servidor
- Su código se envía al navegador
La línea no trata solo sobre rendimiento. Se trata de dónde se ejecuta el código y a qué tiene acceso. Ese enfoque es más útil que pensar en términos de "rápido" vs "lento".
La Obtención de Datos es Donde los RSCs Realmente Brillan
Esta es la parte que genuinamente me encanta. Antes de los RSCs, obtener datos en una aplicación Next.js generalmente significaba una de estas opciones: getServerSideProps, getStaticProps, una llamada useEffect del lado del cliente, o alguna combinación de los tres con un hook personalizado para manejar el estado de carga. Desordenado.
Con RSCs, simplemente... obtienes datos. Dentro del componente. Al nivel superior.
`` async function ProductPage({ id }) { const product = await getProduct(id); // calls your DB directly return <ProductDetails product={product} />; } ``
Sin prop drilling desde una función a nivel de página. Sin spinners de carga para datos que podrían haber estado listos a la llegada. El componente es asincrónico, espera lo que necesita, y se renderiza.
Y aquí es donde se pone genuinamente interesante: porque cada componente de servidor puede obtener sus propios datos de forma independiente, evitas el viejo problema del "componente Dios" donde una función de nivel superior tenía que orquestar todos los datos para toda una página. Los componentes se vuelven autónomos. La obtención paralela ocurre naturalmente cuando esperas Promise.all() o cuando componentes hermanos obtienen datos de forma independiente.
Usé este patrón en un proyecto de panel para una empresa de logística en Birmingham la primavera pasada. Cada widget, estadísticas de envíos, alertas de retraso, resumen de costos, era su propio componente de servidor asincrónico. Sin estado de carga compartido, sin prop drilling, sin condiciones de carrera. La página pasó de un LCP de 2.1 segundos a 0.9 segundos. No solo por los RSCs, pero los RSCs hicieron que la arquitectura correcta fuera mucho más fácil de alcanzar.
La Tubería de Renderizado (Lo Que Realmente Sucede)
Cuando un usuario solicita una página en una aplicación Next.js 14 usando el router app/, aquí está la secuencia aproximada:
- Next.js ejecuta tus componentes de servidor en el servidor
- Esos componentes producen un formato especial llamado React Server Component Payload (RSC Payload), no HTML sin procesar, sino una descripción serializada del árbol de UI
- Next.js usa ese payload para generar el HTML inicial (para el primer pintado)
- Ese HTML se envía al navegador
- El RSC Payload también se envía, y el runtime de React del cliente lo usa para hidratar solo los componentes del cliente en el árbol
- Los componentes del cliente obtienen su JavaScript, se hidratan, y se vuelven interactivos
El paso 5 es lo que hace que los RSCs sean diferentes del SSR plano. El cliente no vuelve a renderizar los componentes del servidor. Solo usa el payload para completar la imagen y luego enfoca el esfuerzo de hidratación en las partes interactivas.
La documentación de Next.js sobre renderizado explica el formato del payload mejor que la mayoría de los posts de blog que he leído, si quieres profundizar.
Dónde los RSCs Se Desmoronan (Crítica Honesta)
Bien. No pretendamos que esto es todo perfecto.
El límite de `"use client"` se propaga. En el momento en que pones "use client" en un componente, cada componente que importa también se vuelve del lado del cliente. Si no tienes cuidado, un manejador onClick puede arrastrar un subárbol sorprendentemente grande al bundle del cliente. He visto esto afectar a desarrolladores junior del equipo varias veces.
Context es complicado. React Context no funciona en componentes de servidor. Si has construido la arquitectura de tu aplicación alrededor de Context para temas, estado de autenticación, o configuración global, necesitarás reestructurar. Hay soluciones (pasar valores como props, usar un wrapper del cliente), pero es fricción que no tenías antes.
La depuración es más difícil. Los errores de componentes de servidor no aparecen en la consola del navegador de la misma manera. El comportamiento del límite de error es diferente. Necesitas revisar los logs del servidor. No es un obstáculo, pero si estás acostumbrado a que todos los errores vivan en DevTools, espera una curva de aprendizaje.
Las librerías de terceros frecuentemente no están listas. Cualquier librería que use hooks o APIs del navegador bajo el capó se romperá en un componente de servidor. Pasarás tiempo envolviendo cosas en archivos "use client" solo para usar un date picker o una librería de animaciones. El ecosistema de React está alcanzando pero a partir de 2024 sigue siendo irregular.
Honestamente, para un sitio de marketing simple o un blog de contenido, quizás no necesites RSCs en absoluto. Si tu equipo está cómodo con el pages router y tu rendimiento es bueno, actualizar solo para soporte de RSC no vale la pena. He disuadido a clientes de migrar más de una vez.
Consejos prácticos para adoptar RSCs en un proyecto existente
Si estás moviendo una app Next.js existente al directorio app/, aquí está el orden que seguiría:
- Empieza con componentes hoja. Los componentes que muestran datos pero no manejan interacción son las victorias más fáciles. Hazlos componentes de servidor primero.
- Identifica tu superficie real de interactividad. Usualmente es más pequeña de lo que crees. Formularios, modales, dropdowns. Ese es tu territorio de
"use client". - Empuja `"use client"` lo más bajo posible. El botón que envía un formulario debería ser un componente cliente. El layout del formulario alrededor probablemente no necesita serlo.
- Audita tu bundle con [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer). Ejecútalo antes y después. Si no ves reducción significativa de bundle, probablemente aún estés enviando demasiado al cliente.
- No compartas módulos solo de servidor. Instala server-only desde npm e impórtalo en la parte superior de cualquier módulo que nunca debería llegar al navegador. Lanzará un error en tiempo de compilación si algo intenta importarlo desde el lado cliente.
El paquete server-only es una cosa pequeña pero nos salvó de un escenario de fuga de datos bastante vergonzoso en un proyecto de cliente de salud. Un desarrollador accidentalmente importó una utilidad de base de datos en un componente que fue marcado como "use client" después. El paquete lo atrapó en tiempo de compilación. Úsalo.
FAQ
¿Los componentes de servidor de React son lo mismo que SSR?
No. SSR (renderizado del lado del servidor) renderiza componentes a HTML en el servidor pero aún envía el JavaScript del componente al cliente para hidratación. Los RSCs se renderizan en el servidor y nunca envían su JavaScript al navegador en absoluto. SSR y RSCs pueden trabajar juntos, y en el directorio app/ de Next.js, lo hacen, pero resuelven problemas diferentes.
¿Necesito Next.js para usar componentes de servidor de React?
Técnicamente no, pero prácticamente sí para la mayoría de equipos. Los RSCs requieren un framework que maneje la infraestructura del servidor, enrutamiento y el pipeline de payload RSC. Next.js 13+ con el directorio app/ es la opción más lista para producción ahora. Remix tiene un modelo diferente. Crear tu propia configuración es posible pero no es un buen uso de tu tiempo a menos que estés construyendo un framework tú mismo.
¿Puedo mezclar componentes de servidor y cliente en la misma página?
Sí, y ese es el punto completo. Una página podría ser un componente de servidor (obtiene datos, sin JS enviado), contener un componente cliente para una entrada de búsqueda, que contiene componentes de servidor como hijos pasados vía prop children. El árbol se intercala. Solo recuerda: los componentes de servidor pueden renderizar componentes cliente, pero los componentes cliente no pueden renderizar componentes de servidor directamente.
¿Qué sucede con mis hooks personalizados existentes?
Los hooks personalizados que usan useState, useEffect o APIs del navegador solo pueden ejecutarse en componentes cliente. No necesitas reescribirlos, solo sé deliberado sobre qué componentes los usan. Si un hook envuelve una llamada de obtención de datos, considera si esos datos podrían obtenerse en un componente de servidor en su lugar y pasarse como props. Frecuentemente pueden, y terminas con código más simple.
¿Hay un costo de rendimiento al formato RSC Payload?
Puede haberlo, particularmente si pasas grandes cantidades de datos a través del payload. El RSC Payload no es lo mismo que JSON, es un formato personalizado, pero aún agrega bytes a tu respuesta. Para la mayoría de apps esto es negligible comparado con los ahorros del bundle de JS. Para apps con conjuntos de datos muy grandes siendo pasados como props, mídelo. No asumas que los RSCs siempre son más pequeños en la red.
---
Los RSCs son un cambio arquitectónico genuino, no solo una nueva API. El modelo mental toma tiempo para establecerse. Dale ese tiempo. Construye algo pequeño con el directorio app/ antes de comprometer un gran proyecto cliente a él. Pasé alrededor de tres semanas en experimentos antes de confiar en mí mismo para enviar arquitectura basada en RSC a producción, y he estado escribiendo React desde 2016.
El vague marketing continuará de personas que no han construido nada real con esto. Ahora al menos sabes lo suficiente para notar la diferencia.
