En React v18 se introdujo el concepto de componente de servidor. Es algo que causó bastante revuelo por la novedad que suponía, pero, pasado un tiempo, ya está llegando el momento en que esto se normaliza y los frameworks que usan React se adaptan a ello.
Es el caso de Next.js, seguramente el primer framework que se nos viene a la cabeza cuando pensamos en React. Next.js v13 nos ha traído muchas novedades, pero una de las más destadas es la introducción por defecto de componentes de servidor, que son componentes que se renderizan en el backend.
Este post pretende ser una simple introducción al concepto de componente de servidor, sus ventajas y cuándo usarlos. Para ello, primero necesitamos entender las diferentes formas en que una página puede ser renderizada cuando usamos Next.js.
Renderizado de páginas
El renderizado es el proceso por el cual el código que escribes se convierte en la interfaz de usuario. Con Next.js tenemos a nuestra disposición diferentes formas de renderizar nuestra aplicación.
Renderizado a nivel de componente
Una aplicación se puede renderizar tanto en el servidor como en el cliente. Esto es así a partir de React 18. Antes, todo se renderizaba directamente en el cliente, lo cual provoca que, a veces, se requiera una carga imortante de código JavaScript en el navegador.
Desde que podemos usar componentes de servidor y componentes de cliente, tenemos el poder de decidir el entorno de renderizado que queramos a nivel de componente. Por defecto, los componentes definidos en el directorio app del proyecto se renderizarán en el servidor, de esta forma se reduce bastante la cantidad de código que se manda al cliente. Eso sí, si nos interesa, podemos definir componentes específicos que se rendericen en el cliente directamente.
Renderizado estático vs dinámico
Next.js nos da opciones para que podamos optimizar el renderizado en el servidor con la posibilidad de hacerlo de forma estática o dinámica.
Renderizado estático
Todos los componentes, los de servidor y los de cliente, se prerenderizan en el servidor en el momento de construir la aplicación (build time). El resultado se cachea y se reutiliza. Como cuando se usa un sitio generado estáticamente (SSG).
Los componentes se renderizan de forma un poco diferente dependiendo de si son de servidor o de cliente.
Los componentes de cliente son prerenderizados y cacheados en el servidor. El resultado se manda al navegador para la hidratación de la aplicación.
Los componentes de servidor se renderizan en el servidor y su resultado se usa para generar el HTML, luego, todo eso se usa para hidratar los componentes en el cliente. De esta forma no es necesario JavaScript desde el lado del navegador.
Renderizado dinámico
Con este tipo de renderizado, tanto los componentes de servidor como los de cliente son renderizados en el servidor en el momento de la petición, cuyo resultado no se cachea (Server-Side Rendering).
Componentes de servidor y de cliente
En un extremo tenemos el gran rendimiento que ofrece el renderizado en el lado del servidor, mientras que en el otro tenemos la gran interactividad que podemos ofrecer en el lado del cliente. Combinando componentes de servidor y de cliente podemos obtener como resultado una aplicación con lo mejor de los dos extremos.
Componentes de servidor
Como hemos comentado antes, todos los componentes dentro de la carpeta app del proyecto serán de servidor por defecto. Si usas Next.js, a partir de la versión 13, no necesitas hacer nada especial para usar este enfoque y beneficiarte del gran rendimiento que ofrece.
La principal ventaja de los componentes de servidor, que hace que ofrezcan un gran rendimiento, es que permiten que muchas dependencias de JavaScript, que antes era necesario gestionar desde el lado del cliente, se queden ahora en el lado del servidor y liberemos al navegador de esa carga. Podemos enviar JavaScript al cliente solo cuando se necesite una interactividad mayor con los componentes de cliente.
Componentes de cliente
Los componentes de cliente se renderizan en el navegador. Usando Next.js, este tipo de componentes se pueden prerenderizar en el servidor y ser hidratados en el cliente.
Qué es la hidratación de componentes
Ya se ha mencionado en este post un par de veces. Puede que te preguntes qué es eso de la hidratación. La hidratación de componentes no es más que una técnica que se usa que consiste en código JavaScript en el lado del cliente que añade dinamismo a una página HTML estática.
¿Cómo se añade este dinamismo? Pues a través de enlazar eventos a elementos HTML, como un clic, pasar el ratón por encima de una zona, al cargar una página, al redimensionarla...
Para conseguir dinamismo en el navegador es necesario, a priori, que tenga cierta carga de trabajo para conseguirlo. A través de la hidratación, conseguimos que haya dinamismo en el navegador ahorrándole esa carga.
Cómo usarlos
Cuando un compoente use algún hook de cliente, como useState por ejemplo, es necesario que en la zona superior del archivo del componente pongas "use client":
"use client" // con esto indicamos que se trata de un componente de cliente import { useState } from "react" const Button = () => { const [active, setActive] = useState(false) } const handleClick = () => { const newActive = !active setActive(newActive) } return ( <button onClick={handleClick}>{active ? "is active" : "is disabled"}</button> ) export default Button
¿Cuándo usar componentes de servidor o de cliente?
Los componentes de servidor se aconsejan para cuando:
- se extraiga información de alguna API externa
- se acceda únicamente a recursos del backend
- queramos usar datos sensibles como API Keys o tokens
- queramos mantener las dependencias en el servidor para reducir la carga en el cliente
Vamos a necesitar componentes de cliente cuando:
- se necesite interactividad a través de eventos como onClick, onChange o similares
- se necesite usar hooks de cliente como useState o useEffect
- queramos utilizar API's del navegador
- queramos usar hooks personalizados que dependan de hooks de cliente o de API's del navegador
- se necesite usar los antiguos componentes de clase
Apunte sobre extracción de datos de API's externas
Aunque se puedan recoger datos conectando con API's externas directamente desde el navegador, se aconseja hacer esta recogida de datos en el servidor a no ser que haya un motivo concreto que obligue a hacerlo en el lado del cliente, ya que a la larga el rendimiento de la aplicación se beneficiará de ello.
Pasando datos entre componentes de servidor y de cliente
Se aconseja obtener los datos en el componente que los necesite en vez de pasarlos entre componentes, pero cuando esto no sea posible y necesitemos pasar información entre un componente de servidor y otro de cliente, lo haremos a través de propiedades, de componentes superiores hacia los inferiores, igual que hasta ahora.
La diferencia es que los datos que se pasen de un componente servidor a un componente cliente tienen que ser serializables, si no lo son no se pueden pasar de forma directa, como ocurre con las funciones o los objetos de fecha.
Resumen
Next.js es uno de los principales frameworks de los que te puedes beneficiar si usas React, aunque no es el único. Para no quedarse atrás, estre framework evoluciona y cada vez nos da más y mejores funcionalidades.
Ahora, a partir de Next.js v13, podemos decidir qué componentes se renderizarán en el servidor y cuáles en el lado del cliente. Esto nos da una flexibilidad enorme para que la aplicación funcione como un cohete sin que penalicemos reduciendo la interactividad de la misma.
Por defecto todos los componentes se renderizarán en el servidor, pero podremos indicar de forma explícita los componentes que se tengan que renderizar en el cliente.