BackVolver al blog

Next.js 9.3

Next.js 9.3 introduce mejoras en la generación de sitios estáticos, soporte nativo para SCSS, reducción en el tamaño de los bundles, páginas 404 estáticas y más.

Hoy estamos emocionados de presentar Next.js 9.3, que incluye:

Todos estos beneficios son compatibles con versiones anteriores y no rompen cambios. Para actualizar solo necesitas ejecutar:

Terminal
npm i next@latest react@latest react-dom@latest

Soporte Mejorado para Generación de Sitios Estáticos (SSG)

Al construir sitios web o aplicaciones, generalmente tienes que elegir entre 2 estrategias: Generación estática (SSG) o renderizado del lado del servidor (SSR).

Next.js es el primer framework híbrido, permitiéndote elegir la técnica que mejor se adapte a tu caso de uso por cada página.

Next.js 9.0 introdujo el concepto de Optimización Estática Automática. Cuando una página no tiene requisitos de obtención de datos bloqueantes como getInitialProps, se renderizará automáticamente a HTML en tiempo de construcción.

Hay más casos donde podrías querer renderizar una página a HTML estático en tiempo de construcción, incluso con requisitos de obtención de datos bloqueantes. Un ejemplo de esto son páginas de marketing impulsadas por un Sistema de Gestión de Contenidos (CMS) o la sección de blog de un sitio.

Hemos colaborado con usuarios intensivos de SSG y next export como HashiCorp y discutido extensamente las restricciones adecuadas con la comunidad en el RFC más comentado en la historia de Next.js para crear una nueva forma unificada de realizar obtención de datos y generación estática.

Hoy estamos increíblemente emocionados de anunciar dos nuevos métodos de obtención de datos: getStaticProps y getServerSideProps. También incluimos una forma de proporcionar parámetros para generar páginas estáticas para rutas dinámicas: getStaticPaths.

Estos nuevos métodos tienen muchas ventajas sobre el modelo getInitialProps ya que hay una clara distinción entre lo que será SSG vs SSR.

  • getStaticProps (Generación Estática): Obtiene datos en tiempo de construcción.

  • getStaticPaths (Generación Estática): Especifica rutas dinámicas para prerrenderizar basadas en datos.

  • getServerSideProps (Renderizado del Lado del Servidor): Obtiene datos en cada solicitud.

  • Estas mejoras son adiciones a la API. Toda la nueva funcionalidad es completamente compatible con versiones anteriores y puede adoptarse incrementalmente. No se introducen depreciaciones y getInitialProps continuará funcionando como lo hace actualmente. Recomendamos adoptar estos nuevos métodos en nuevas páginas y proyectos.

getStaticProps

Si exportas una función async llamada getStaticProps desde una página, Next.js prerrenderizará esta página en tiempo de construcción. Esto es especialmente útil cuando deseas renderizar páginas estáticas específicas desde un CMS.

getStaticProps siempre se ejecuta en el contexto de Node.js y el código se elimina automáticamente de los bundles del navegador, asegurando que se envíe menos código al navegador. De esta manera no tienes que preocuparte por la ejecución del código de obtención de datos en ambos entornos Node.js y navegador, que tienen algunas inconsistencias.

Esto te permite usar cualquier técnica de obtención de datos asíncrona o incluso síncrona, incluyendo fetch, REST, GraphQL, o incluso acceder directamente a una base de datos.

pages/posts/[id].js
export async function getStaticProps(context) {
  return {
    props: {}, // se pasará al componente de la página como props
  };
}

El parámetro context es un objeto que contiene las siguientes claves:

  • params: params contiene los parámetros de ruta para páginas que usan rutas dinámicas. Por ejemplo, si el nombre de la página es [id].js, entonces params se verá como { id: ... }. Para aprender más, consulta la documentación de Enrutamiento Dinámico. Debes usar esto junto con getStaticPaths, que explicaremos más adelante.

Aquí hay un ejemplo que usa getStaticProps para obtener una lista de publicaciones de blog desde un CMS:

pages/blog.js
// Puedes usar cualquier biblioteca de obtención de datos
import fetch from 'node-fetch';
 
// posts se llenará en tiempo de construcción por getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}
 
// Esta función se llama en tiempo de construcción en el entorno Node.js.
// No se llamará en el lado del cliente, por lo que incluso puedes hacer
// consultas directas a la base de datos. Ver la sección "Detalles técnicos".
export async function getStaticProps() {
  // Llama a un endpoint API externo para obtener las publicaciones.
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // Al devolver { props: posts }, el componente Blog
  // recibirá `posts` como prop en tiempo de construcción
  return {
    props: {
      posts,
    },
  };
}
 
export default Blog;

¿Cuándo debo usar getStaticProps?

Deberías usar getStaticProps si:

  • Los datos necesarios para renderizar la página están disponibles en tiempo de construcción antes de la solicitud de un usuario.
  • Los datos provienen de un CMS headless.
  • Los datos pueden almacenarse en caché públicamente (no específicos del usuario).
  • La página debe prerrenderizarse (para SEO) y ser muy rápida — getStaticProps genera archivos HTML y JSON, ambos pueden almacenarse en caché por un CDN para mejor rendimiento.

Para aprender más sobre getStaticProps consulta la Documentación de Obtención de Datos.

getStaticPaths

Si una página tiene rutas dinámicas y usa getStaticProps, necesita definir una lista de rutas que deben renderizarse a HTML en tiempo de construcción.

Si exportas una función async llamada getStaticPaths desde una página que usa rutas dinámicas, Next.js prerrenderizará estáticamente todas las rutas especificadas por getStaticPaths.

pages/posts/[id].js
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // Ver la sección "paths" abajo
    ],
    fallback: true or false // Ver la sección "fallback" abajo
  };
}

La clave paths (requerida)

La clave paths determina qué rutas se prerrenderizarán. Por ejemplo, supongamos que tienes una página que usa rutas dinámicas llamada pages/posts/[id].js. Si exportas getStaticPaths desde esta página y devuelves lo siguiente para paths:

return {
  paths: [
    { params: { id: 1 } },
    { params: { id: 2 } }
  ],
  fallback: ...
}

Entonces Next.js generará estáticamente posts/1 y posts/2 en tiempo de construcción usando el componente de página en pages/posts/[id].js.

Nota que el valor para cada params debe coincidir con los parámetros usados en el nombre de la página:

  • Si el nombre de la página es pages/posts/[postId]/[commentId], entonces params debe contener postId y commentId.
  • Si el nombre de la página usa rutas catch-all, por ejemplo pages/[...slug], entonces params debe contener slug que es un array. Por ejemplo, si este array es ['foo', 'bar'], entonces Next.js generará estáticamente la página en /foo/bar.

La clave fallback (requerida)

El objeto devuelto por getStaticPaths debe contener una clave booleana fallback.

Fallback: false

Si fallback es false, entonces cualquier ruta no devuelta por getStaticPaths resultará en una página 404. Esto es útil si sabes que todas las rutas se conocerán en tiempo de construcción.

Aquí hay un ejemplo que prerrenderiza una publicación de blog por página llamada pages/posts/[id].js. La lista de publicaciones se obtendrá desde un CMS y se devolverá por getStaticPaths. Luego, para cada página, obtiene los datos de la publicación desde un CMS usando getStaticProps.

pages/posts/[id].js
import fetch from 'node-fetch';
 
function Post({ post }) {
  // Renderizar publicación...
}
 
// Esta función se llama en tiempo de construcción
export async function getStaticPaths() {
  // Llama a un endpoint API externo para obtener las publicaciones
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // Obtiene las rutas que queremos prerrenderizar basadas en las publicaciones
  const paths = posts.map((post) => `/posts/${post.id}`);
 
  // Solo prerrenderizaremos estas rutas en tiempo de construcción.
  // { fallback: false } significa que otras rutas deberían devolver 404.
  return { paths, fallback: false };
}
 
// Esto también se llama en tiempo de construcción
export async function getStaticProps({ params }) {
  // params contiene el `id` de la publicación.
  // Si la ruta es como /posts/1, entonces params.id es 1
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();
 
  // Pasa los datos de la publicación a la página via props
  return { props: { post } };
}
 
export default Post;

Fallback: true

Si fallback es true, entonces el comportamiento de getStaticProps cambia, Next.js renderizará las rutas proporcionadas a HTML en tiempo de construcción. Cuando una ruta no fue generada en tiempo de construcción, se generará bajo demanda cuando un usuario solicite la página.

Esto es útil cuando tu aplicación tiene muchas rutas que pueden generarse estáticamente pero no quieres incurrir en tiempos de construcción incrementados para páginas generando solo un subconjunto en tiempo de construcción.

El usuario que desencadene la generación de la página recibirá un HTML de fallback, generalmente una página con un estado de carga. La razón de esto es que el HTML estático puede servirse desde un CDN, asegurando que la página siempre sea rápida, incluso cuando aún no se ha generado.

Un ejemplo de generación estática bajo demanda de páginas adicionales:

pages/posts/[id].js
import { useRouter } from 'next/router';
import fetch from 'node-fetch';
 
function Post({ post }) {
  const router = useRouter();
 
  // Si la página aún no se ha generado, esto se mostrará
  // inicialmente hasta que getStaticProps() termine de ejecutarse
  if (router.isFallback) {
    return <div>Cargando...</div>;
  }
 
  // Renderizar publicación...
}
 
// Esta función se llama en tiempo de construcción
export async function getStaticPaths() {
  return {
    // Solo `/posts/1` y `/posts/2` se generan en tiempo de construcción
    paths: [{ params: { id: 1 } }, { params: { id: 2 } }],
    // Habilita la generación estática de páginas adicionales
    // Por ejemplo: `/posts/3`
    fallback: true,
  };
}
 
// Esto también se llama en tiempo de construcción
export async function getStaticProps({ params }) {
  // params contiene el `id` de la publicación.
  // Si la ruta es como /posts/1, entonces params.id es 1
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();
 
  // Pasa los datos de la publicación a la página via props
  return { props: { post } };
}
 
export default Post;

Para aprender más sobre getStaticPaths consulta la Documentación de Obtención de Datos.

getServerSideProps

Si exportas una función async llamada getServerSideProps desde una página, Next.js renderizará esta página en cada solicitud (Renderizado del lado del servidor - SSR).

getServerSideProps siempre se ejecuta en el servidor y el código se elimina automáticamente de los paquetes del navegador (tree-shaken), asegurando que se envíe menos código al navegador. De esta manera, no tienes que preocuparte por la ejecución del código de obtención de datos en entornos tanto del servidor como del navegador, que tienen algunas inconsistencias. Esto mejora el rendimiento en muchos casos, ya que el servidor generalmente tendrá una conexión más rápida a la fuente de datos. También aumenta la seguridad al exponer menos lógica de obtención de datos.

Esto te permite utilizar cualquier técnica de obtención de datos asíncrona o incluso síncrona, incluyendo fetch, REST, GraphQL o incluso acceder directamente a una base de datos.

Al navegar entre páginas usando next/link, en lugar de ejecutar getServerSideProps en el navegador, Next.js hará una solicitud al servidor que devolverá el resultado de llamar a getServerSideProps.

pages/index.js
export async function getServerSideProps(context) {
  return {
    props: {}, // se pasará al componente de la página como props
  };
}

El parámetro context es un objeto que contiene las siguientes claves:

Aquí hay un ejemplo que utiliza getServerSideProps para obtener datos en el momento de la solicitud y renderizarlos:

pages/index.js
function Page({ data }) {
  // Renderizar datos...
}
 
// Esto se llama en cada solicitud
export async function getServerSideProps() {
  // Obtener datos de una API externa
  const res = await fetch(`https://.../data`);
  const data = await res.json();
 
  // Pasar los datos a la página mediante props
  return { props: { data } };
}
 
export default Page;

Para más información sobre getServerSideProps, consulta la Documentación de Obtención de Datos.

Modo de Vista Previa

Como se mencionó anteriormente en esta publicación, la Generación Estática es útil cuando tus páginas obtienen datos de un CMS sin interfaz (headless CMS). Sin embargo, no es ideal cuando estás escribiendo un borrador en tu CMS y deseas previsualizarlo inmediatamente en tu página. Como la salida es estática, previsualizar cambios se vuelve más difícil, ya que tendrías que regenerar esa página estática.

La introducción de getStaticProps en Next.js abre nuevas posibilidades, como aprovechar las capacidades de renderizado bajo demanda de Next.js bajo ciertas condiciones.

Por ejemplo, al previsualizar un borrador desde tu CMS, querrías omitir el renderizado estático y renderizar la página bajo demanda con el contenido del borrador en lugar del contenido publicado. Querrías que Next.js omita la Generación Estática solo para este caso específico.

Nos complace anunciar una nueva función integrada de Next.js para abordar esta necesidad: el Modo de Vista Previa.

El Modo de Vista Previa permite a los usuarios omitir la página generada estáticamente para renderizar bajo demanda (SSR) una página de borrador desde, por ejemplo, un CMS.

Sin embargo, no estás limitado solo a ciertos sistemas de CMS. El Modo de Vista Previa se integra directamente con getStaticProps y getServerSideProps, por lo que puede usarse con cualquier tipo de solución de obtención de datos.

El Modo de Vista Previa ya está disponible al usar next start, o de manera fluida al desplegar en la Red Edge de Vercel.

Prueba el modo de vista previa tú mismo en https://next-preview.vercel.app/

Aprende más sobre el Modo de Vista Previa consultando la documentación.

Colaboración con proveedores de CMS

getStaticProps te permite obtener datos de cualquier fuente de datos, incluidos sistemas de CMS.

Estamos colaborando activamente con muchos de los actores clave en el ecosistema de CMS para proporcionar ejemplos y guías sobre integración con Next.js.

Ejemplos en los que actualmente estamos trabajando incluyen:

Si tu empresa está activa en el ecosistema de CMS, ¡nos encantaría trabajar contigo! No dudes en contactar a nuestro equipo por correo electrónico o Twitter.

Soporte integrado para Sass en hojas de estilo globales

Next.js 9.2 introdujo soporte integrado para hojas de estilo CSS globales para reemplazar el plugin next-css con mejores valores predeterminados y proporcionar un resultado más optimizado.

Poco después del lanzamiento, recibimos cada vez más solicitudes para integrar soporte para Sass, ya que muchas empresas que migran a Next.js tienen un sistema de diseño existente basado en Sass.

Al investigar el uso de plugins en Next.js, descubrimos que aproximadamente el 30% de las aplicaciones Next.js usan next-sass hoy en día. En comparación con el 44% que usa CSS vanilla y el 6% que usa Less.

Además, next-sass tenía las mismas limitaciones que next-css. Esto significaba que podías importar un archivo Sass en cada archivo del proyecto, sin embargo, este archivo Sass importado sería global para toda la aplicación.

Después de considerar estas estadísticas y comentarios, nos complace anunciar que Next.js ahora tiene soporte integrado para importar hojas de estilo Sass.

Para comenzar a usar importaciones globales de Sass en tu aplicación, instala sass:

Terminal
npm install sass

Luego, importa el archivo Sass dentro de pages/_app.js.

Por ejemplo, considera la siguiente hoja de estilo llamada styles.scss en la raíz de tu proyecto:

$primary-color: #333;
 
body {
  padding: 20px 20px 60px;
  margin: 0;
  color: $primary-color;
}

Crea un archivo pages/_app.js si aún no está presente. Luego, importa el archivo styles.scss:

pages/_app.js
import '../styles.scss';
 
// Esta exportación predeterminada es requerida en un nuevo archivo `pages/_app.js`.
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

Dado que las hojas de estilo son globales por naturaleza, deben importarse en el componente <App> personalizado. Esto es necesario para evitar conflictos de nombres de clases y orden para estilos globales.

En desarrollo, expresar hojas de estilo de esta manera permite que tus estilos se actualicen automáticamente en la página a medida que los editas.

En producción, todos los archivos Sass y CSS se concatenarán automáticamente en un único archivo .css minimizado. Este archivo CSS se cargará mediante una etiqueta <link> y se inyectará automáticamente en el marcado HTML predeterminado que genera Next.js.

Esta nueva función es totalmente compatible con versiones anteriores. Si estás usando @zeit/next-sass u otros plugins relacionados con CSS, la función se desactiva para evitar conflictos.

Si actualmente estás usando @zeit/next-sass, te recomendamos eliminar el plugin de tu next.config.js y package.json, migrando así al soporte integrado de Sass al actualizar.

Soporte integrado para Módulos CSS con Sass para estilos a nivel de componente

Next.js ahora admite Módulos CSS con archivos Sass usando la convención de nombres de archivo [name].module.scss.

A diferencia del soporte disponible anteriormente en Next.js 5+ usando next-sass, ahora los archivos Sass globales y los módulos CSS pueden coexistirnext-sass requería que todos los archivos .scss en tu aplicación se manejaran como globales o locales, pero no ambos.

Los Módulos CSS limitan el alcance de Sass localmente al crear automáticamente nombres de clase únicos. Esto te permite usar el mismo nombre de clase Sass en diferentes archivos sin preocuparte por colisiones.

Este comportamiento hace que los Módulos CSS sean la forma ideal de incluir Sass a nivel de componente. Los archivos de Módulos CSS se pueden importar en cualquier parte de tu aplicación.

Para comenzar a usar Módulos CSS con Sass en tu aplicación, asegúrate de tener sass instalado:

Terminal
npm install sass

Ahora, considera un componente reutilizable Button en la carpeta components/:

Primero, crea components/Button.module.scss con el siguiente contenido:

/*
No necesitas preocuparte por que .error {} colisione con cualquier otro archivo `.css` o
`.module.css`!
*/
$color: white;
 
.error {
  color: $color;
  background-color: red;
}

Luego, crea components/Button.js, importando y usando el archivo CSS anterior:

components/Button.js
import styles from './Button.module.scss';
 
export function Button() {
  return (
    <button
      type="button"
      // Observa cómo la clase "error" se accede como una propiedad en el objeto
      // `styles` importado.
      className={styles.error}
    >
      Destroy
    </button>
  );
}

Los Módulos CSS para archivos Sass son una función opcional y solo se habilitan para archivos con la extensión .module.scss. Todavía se admiten las hojas de estilo <link> regulares y los estilos Sass globales.

En producción, todos los archivos de Módulos CSS se concatenan automáticamente en muchos archivos .css minimizados y divididos por código. Estos archivos .css representan rutas de ejecución calientes en tu aplicación, asegurando que se cargue la cantidad mínima de CSS por página para que tu aplicación se renderice.

Como se mencionó anteriormente, esta nueva función es totalmente compatible con versiones anteriores. Si estás usando @zeit/next-sass u otros plugins relacionados con CSS, la función se desactiva para evitar conflictos.

Si actualmente estás usando @zeit/next-sass, te recomendamos eliminar el plugin de tu next.config.js y package.json, migrando así al soporte integrado de Sass.

Optimización Estática Automática para 404

El lanzamiento de Next.js 9 introdujo el concepto de Optimización Estática Automática cuando una página no tiene requisitos de datos bloqueantes, Next.js generará automáticamente la página como HTML estático en el momento de la construcción. Sin embargo, había una página que no se renderizaba automáticamente como HTML estático: la página 404. La razón principal por la que la página 404 no se hacía estática automáticamente era que la página /_error que maneja el 404 también gestionaba más cosas, como errores.

Dado que las páginas 404 se renderizan para rutas inexistentes, renderizar la página bajo demanda podría aumentar el costo y la carga del servidor.

Nos propusimos facilitar el éxito de dos maneras:

  • La experiencia predeterminada de Next.js genera una página 404 estática
  • Al personalizar la página 404, aún se asegura que termines con una página estática

Esta función es totalmente compatible con versiones anteriores, por lo que si actualmente tienes un pages/_error.js personalizado, seguirá usándose para la página 404 hasta que agregues pages/404.js.

Página 404 estática por defecto

Cuando tu aplicación no tiene una página personalizada pages/_error.js, Next.js generará automáticamente la página 404 estática y la usará cuando sea necesario servir un 404. Esto sucede automáticamente y no se requieren cambios.

Página 404 personalizada usando pages/404.js

Para sobrescribir la página 404 predeterminada, ahora puedes crear un pages/404.js que seguirá siendo optimizado estáticamente en el momento de la construcción. Esta página se usa en lugar de pages/_error.js para renderizar un 404 si tu aplicación tiene una.

pages/404.js
export default () => <h1>Esta es la página 404</h1>;

Runtime 32+ kB más pequeño (15 kB+ Gzip)

Next.js admite los mismos navegadores que React, sin configuración requerida. Esto incluye Internet Explorer 11 (IE11) y todos los navegadores populares (Edge, Firefox, Chrome, Safari, Opera, etc.).

Como parte de esta compatibilidad, también compilamos tu aplicación para que sea compatible con IE11: esto te permite usar de manera segura características de sintaxis ES6+, Async/Await, Propiedades de Rest/Spread de Objetos y más, todo sin necesidad de configuración.

Parte de este proceso de compilación también implica inyectar transparentemente los polyfills necesarios (por ejemplo, Array.from o Symbol). Sin embargo, estos polyfills solo son necesarios para menos del 10% del tráfico web, en la mayoría de los casos para admitir IE11.

A partir de Next.js 9.3, Next.js cargará automáticamente los polyfills necesarios para admitir navegadores heredados, y solo cargará los polyfills en estos navegadores heredados.

En la práctica, esto significa que se eliminarán 32 kB o más del tamaño de tu Primera Carga para más del 90% de tus usuarios.

Estos ahorros de tamaño son aún mayores para aplicaciones más grandes que dependen de aún más características del navegador.

¡Esta optimización es totalmente automática y no se necesitan cambios en la aplicación para aprovecharla!

Comunidad

Estamos muy emocionados de ver el crecimiento continuo en la adopción de Next.js:

  • Hemos tenido más de 927 colaboradores independientes.
  • En GitHub, el proyecto ha sido marcado con estrella más de 46,600 veces.
  • El directorio de ejemplos tiene más de 226 ejemplos.

La comunidad de Next.js ahora tiene más de 15,250 miembros. La comunidad ahora se puede encontrar en las discusiones de GitHub, un nuevo lugar para que la comunidad discuta y haga preguntas. ¡Únete a nosotros!

Estamos agradecidos con nuestra comunidad y todos los comentarios y contribuciones externas que ayudaron a dar forma a este lanzamiento.

Un agradecimiento especial a Jeff Escalante por sus valiosos comentarios sobre los nuevos métodos de obtención de datos.

Un enorme agradecimiento a todos los que contribuyeron a este lanzamiento: @arcanis, @lgordey, @ijjk, @martpie, @jaywink, @fabianishere, @dijs, @TheRusskiy, @quinnturner, @timneutkens, @lfades, @vvo, @adithwip, @rafaelalmeidatk, @bmathews, @Spy-Seth, @EvgeniyKumachev, @chibicode, @piglovesyou, @HaNdTriX, @Timer, @janicklas-ralph, @devknoll, @prateekbh, @ethanryan, @MoOx, @rifaidev, @msweeneydev, @motiko y @balazsorban44 por su ayuda.