React Server Components explicados: use client, Server Actions, Suspense | We.Inc
React Server Components explicados con claridad: un diagrama de fronteras, las reglas de "use client", la obtención de datos, server actions para formularios con useActionState, streaming con Suspense y soluciones a los errores habituales.
React Server Components explicados
Los React Server Components se renderizan en el servidor y envían al navegador la UI terminada, no su JavaScript. Pueden hacer await de datos y usar secretos directamente. Todo lo interactivo va en un Client Component, un archivo que empieza con "use client". Los formularios pueden llamar a funciones de servidor marcadas con "use server", y Suspense envía por streaming las partes lentas más tarde.
Las tres directivas y hooks que necesitas
Escribes
Significa
(nada)
Server Component, en el App Router de Next.js
"use client"
Este archivo y lo que importa se ejecutan también en el navegador
"use server"
Estas funciones exportadas se ejecutan en el servidor cuando se las invoca
<Suspense>
Muestra aquí un fallback mientras carga esta parte
Empieza por el diagrama: las cajas azules se quedan en el servidor, las naranjas también se envían al navegador. Después copia los patrones de datos, formularios y streaming, y usa la tabla de errores cuando el build se queje.
Deja los componentes como Server Components por defecto.
Obtén los datos en Server Components async, cerca de donde se muestran.
Añade "use client" solo a las partes pequeñas que necesitan estado o manejadores.
Usa funciones de servidor para formularios y mutaciones, con useActionState para dar feedback.
Envuelve las partes lentas en Suspense, o añade un loading.tsx, para que la página aparezca rápido.
Baja "use client" lo más que puedas en el árbol; una frontera cerca de la raíz vuelve a convertir toda la app en código de cliente.
Valida cada entrada de las funciones de servidor. Es un endpoint público, aunque solo lo llame tu formulario.
Comprueba quién es el usuario dentro de cada función de servidor, no solo en la página que muestra el formulario.
Pasa los Server Components a los Client Components como children en lugar de importarlos.
Evita las cascadas de peticiones: lanza juntas las peticiones independientes con Promise.all.
Preguntas frecuentes
¿Los Server Components son lo mismo que el renderizado en servidor?
No. El renderizado en servidor convierte cualquier componente en HTML para la primera carga y sigue enviando su JavaScript. Un Server Component nunca envía su JavaScript; solo envía su resultado. Los Client Components también se renderizan en el servidor para la primera carga.
¿Necesito Next.js para usar Server Components?
Necesitan un framework o un bundler que los admita. El App Router de Next.js es el más usado; algunos otros, como Waku, también los admiten.
¿Un Client Component puede importar un Server Component?
No, una importación desde un archivo de cliente mete el módulo en el bundle del cliente. En su lugar, pasa el Server Component como children u otra prop desde un Server Component padre.
¿Qué diferencia hay entre server actions y funciones de servidor?
React denomina ahora función de servidor a cualquier función marcada con "use server". Cuando se usa como action de un formulario o se invoca dentro de una transición, se le suele llamar server action. Next.js fue el primero en usar el término server actions.
¿useActionState funciona sin JavaScript?
El formulario sigue enviándose a la función de servidor antes de la hidratación, así que se envía. El estado pendiente y el mensaje en línea necesitan JavaScript, así que asegúrate de que el servidor devuelva una página útil en cualquier caso.
Por qué este modelo ayuda a una web
La mayor parte de una página de marketing o de contenido no cambia nunca después de renderizarse: títulos, textos, imágenes, tarjetas de producto. Como Server Components no cuestan nada de JavaScript, lo que deja libre el hilo principal para las pocas partes interactivas y suele mejorar el Interaction to Next Paint.
Lista de seguridad para funciones de servidor
Trata cada función "use server" como una ruta de API: comprueba la sesión, valida la entrada con un esquema, comprueba que el usuario puede cambiar ese registro y devuelve solo lo que el cliente necesita. Nunca devuelvas filas de la base de datos con campos que el usuario no deba ver.
Qué se ejecuta dónde
Dónde puede ejecutarse cada cosa
Necesitas
Server Component
Client Component
Leer una base de datos o una clave de API secreta
Sí
No (expondría el secreto)
Hacer await de datos directamente en el componente
Sí (componente async)
No
useState, useEffect, useReducer
No
Sí
onClick, onChange y otros manejadores
No
Sí
window, localStorage, APIs del navegador
No
Sí, después del montaje
No enviar nada de JavaScript del componente
Sí
No
Renderizar el otro tipo
Sí, renderiza Client Components
Solo Server Components recibidos como children o props
Server Components y Client Components
Un Server Component que lee datos
En el App Router, los componentes son Server Components salvo que se indique lo contrario. Pueden ser async y hablar directamente con la base de datos; nada de este código llega al navegador.
Un Client Component para la interacción
"use client" al principio del todo marca una frontera: este archivo y todo lo que importa se empaqueta para el navegador. Mantén estos archivos pequeños y llévalos a las hojas del árbol.
Pasar un Server Component a través de un Client Component
Un Client Component no puede importar un Server Component, pero sí recibirlo como children. El servidor lo renderiza primero y el cliente solo coloca el resultado.
Que los secretos no lleguen al navegador por accidente
Importar el paquete server-only hace que el build falle si algún archivo de cliente importa este módulo, así que una clave de API no puede filtrarse por una importación descuidada.
Funciones de servidor para formularios
Un formulario que se envía a una función de servidor
"use server" convierte una función async en un endpoint al que el formulario puede llamar. El formulario funciona antes de que cargue JavaScript, porque es un envío de formulario real.
Estado pendiente y resultado con useActionState
useActionState (React 19) envuelve la función de servidor y te da su último resultado y un indicador de pendiente, así que el botón puede decir Enviando y el mensaje puede aparecer sin estado extra.
Refrescar los datos de la página tras un cambio
Después de una mutación, indica a Next.js qué ruta tiene datos obsoletos para que el siguiente render muestre el cambio.
Streaming con Suspense
Envía por streaming la parte lenta con Suspense
Envuelve un Server Component lento en Suspense. El resto de la página se envía de una vez con el fallback en su sitio, y la parte lenta llega por streaming cuando llegan sus datos.
loading.tsx para una ruta entera
Un archivo loading.tsx junto a page.tsx envuelve esa página en una frontera de Suspense por ti, así que la navegación lo muestra al instante mientras cargan los datos de la página.
Lanza las peticiones juntas
Esperar una petición tras otra en un Server Component suma sus tiempos. Lánzalas a la vez y espera a las dos.
Errores con los que te vas a encontrar
Errores habituales y su solución
Lo que ves
Causa
Solución
Un hook como useState solo funciona en un Client Component
Un hook en un archivo sin "use client"
Mueve la parte interactiva a su propio archivo que empiece con "use client"
No se pueden pasar manejadores de eventos a las props de un Client Component
onClick pasado desde un Server Component
Pon el manejador dentro del Client Component, o pasa en su lugar una función de servidor
No se pueden pasar funciones directamente a Client Components
Una función normal pasada como prop a través de la frontera
Márcala con "use server" si debe ejecutarse en el servidor, o defínela en el archivo de cliente
Solo se pueden pasar objetos simples a Client Components
Instancias de clase, como modelos cargados de Date o Maps, enviadas como props
Pasa datos simples (strings, números, arrays, objetos simples)
window is not defined
Una API del navegador usada al renderizar en el servidor
Léela dentro de useEffect, o comprueba antes typeof window
Error de hidratación (hydration mismatch)
El servidor y el navegador renderizaron texto distinto (fechas, valores aleatorios)
Renderiza la parte cambiante solo en el cliente, o pasa el valor desde el servidor