Skip to content

Next JS Fundamentos - Guía para la versión 13

Exploraremos las novedades en la versión 13 de Next.js, que ofrece una gama de características y mejoras para optimizar el desarrollo de aplicaciones web.

Además, discutiremos el enfoque de React Server Components y cómo estos se integran en Next.js para ofrecer una experiencia de desarrollo mejorada y un rendimiento optimizado.

También veremos el proceso de instalación y cómo configurar el enrutamiento en aplicaciones Next.js utilizando la convención de nombres y enrutamiento centrado en el servidor.

Al final, obtendrás una comprensión sólida de las características clave de Next.js 13 y cómo aprovecharlas para construir aplicaciones web modernas y eficientes.

⭐ Videos Premium ⭐

Esta sección es parte del curso en Udemy. Si quieres acceder a ella, puedes comprar el curso en Udemy: React + Firebase by bluuweb.

Recursos

Requisitos

Versiones

Actualmente Next JS se encuentra en la versión 13 (abril 2023) y existen dos formas de trabajar con esta versión:

App Router (actualmente en beta)

  • Routing: Nuevo enrutador basado en un sistema de archivos creado sobre los componentes del servidor que admite layout, enrutamiento anidado, estados de carga (loading), manejo de errores y más.
  • Rendering: Client-side and Server-side Rendering (utilizando Client y Server components). Optimizado aún más con renderización estática y dinámica en el servidor con Next.js.
  • Data Fetching: Obtención de datos simplificada con soporte async/await en los componentes de React y Fetch en el lado del cliente y el servidor.
  • Caching: Nueva Next.js HTTP Cache y client-side cache optimizado para los componentes de servidor y navegación del lado del cliente.
  • Optimizations: Componente de imagen mejorado con carga diferida del navegador nativo. Nuevo módulo de fuentes con optimización automática de fuentes.
  • Transpilation: Transpilación automática y agrupación de dependencias de paquetes locales (como monorepos) o de dependencias externas (node_modules).
  • API: Consulte la sección de referencia de API para conocer las nuevas API.
  • Tooling: Turbopack, un reemplazo de Webpack basado en Rust hasta 700 veces más rápido.

TIP

Transpilation: Automatic transpilation and bundling of dependencies from local packages (like monorepos) or from external dependencies (node_modules).

La frase describe un proceso en el cual se transpila automáticamente el código fuente y se agrupan las dependencias, ya sean locales (monorepos) o externas (node_modules), para facilitar la distribución y compatibilidad de aplicaciones web.

"Automatic transpilation" se refiere a la conversión automática de código fuente de un lenguaje o sintaxis a otro. En el caso de JavaScript, esto podría implicar la transpilación de código fuente escrito en una versión más reciente del lenguaje (como ECMAScript 6) a una versión más antigua (como ECMAScript 5) que sea compatible con navegadores web más antiguos.

"Bundling of dependencies" hace referencia a la agrupación de múltiples archivos de código fuente y sus dependencias en un solo archivo o en un número reducido de archivos. Esto facilita la distribución y carga de la aplicación en navegadores web, ya que se reduce el número de solicitudes HTTP necesarias para obtener todos los archivos requeridos.

El término "local packages" se refiere a paquetes de código fuente que están disponibles localmente en el proyecto, como en un monorepositorio. Un monorepositorio es un enfoque de organización del código fuente en el que múltiples proyectos o paquetes se almacenan en un único repositorio de control de versiones.

Finalmente, "external dependencies" son paquetes de terceros que un proyecto puede necesitar para funcionar correctamente, como librerías y módulos de código. Estas dependencias suelen ser descargadas e instaladas en la carpeta "node_modules" cuando se utiliza el administrador de paquetes de Node.js (npm o yarn, por ejemplo).

Pensando en componentes de servidor

React Server Components presenta un nuevo modelo mental para crear aplicaciones híbridas que aprovechan el servidor y el cliente.

En lugar de que React renderice toda su aplicación del lado del cliente, React ahora le brinda la flexibilidad de elegir dónde renderizar sus componentes en función de su propósito.

ejemplo página con next

Si tuviéramos que dividir la página en componentes más pequeños, notará que la mayoría de los componentes no son interactivos y se pueden representar en el servidor como componentes del servidor.

Para piezas más pequeñas de interfaz de usuario interactiva, podemos agregar componentes de cliente. Esto se alinea con el enfoque de servidor primero.

Para facilitar esta transición, los componentes del servidor son los predeterminados en el directorio app.

Instalación

sh
npx create-next-app@latest --experimental-app

Rounting

ejemplo routing next 13

  • Las carpetas se utilizan para definir rutas. Una ruta es una ruta única de carpetas anidadas, siguiendo la jerarquía desde la carpeta raíz hasta una carpeta final que incluye un archivo page.js.
  • Archivos especiales: existen otros tipos para los layout, not-found, errores, etc.

TIP

De forma predeterminada, los componentes internos al directorio app son React Server Components.

Convención de nombres

Next.js proporciona un conjunto de archivos especiales para crear UI con un comportamiento específico en rutas anidadas:

  • page.js: interfaz de usuario pública.
    • route.js: puntos finales para API del lado del servidor.
  • layout.js: diseño compartido para sus páginas.
    • template.js: similar a layout pero se monta una nueva instancia al momento de navegar entre rutas.
  • not-found.js: página 404 personalizada si es que ninguna ruta coincide.
  • loading.js: página de carga personalizada.
  • error.js: página de error personalizada.
    • global-error.js: Similar a error.js, pero específicamente para detectar errores del layourt raíz.

Al utilizar el enrutamiento centrado en el servidor, cuando se carga una página por primera vez, el servidor se encarga de renderizar la página y enviarla al cliente. Esto optimiza la carga inicial de la página, ya que el cliente no tiene que descargar un mapa de rutas completo.

Sin embargo, una vez que la página se ha cargado en el navegador del usuario, la aplicación Next.js utiliza la navegación del lado del cliente para mejorar la experiencia del usuario. El componente de enlace mencionado (por ejemplo, Link de Next.js) es responsable de gestionar la navegación entre rutas sin recargar toda la página.

ejemplo representación parcial next

Esta combinación de enrutamiento centrado en el servidor y enrutamiento del lado del cliente permite una experiencia similar a la de una aplicación de una sola página (SPA), donde la navegación entre rutas es rápida y sin recargas de página completas. Sin embargo, para aclarar, cuando un usuario navega entre las páginas dentro de la aplicación, sí se realizan solicitudes al servidor, pero estas solicitudes son para obtener solo los datos necesarios para actualizar los componentes de la página en lugar de cargar toda la página nuevamente. Esto hace que la navegación sea más eficiente y mejora la experiencia del usuario.

Finalmente, a medida que los usuarios navegan por la aplicación, el enrutador almacenará en caché las páginas que visitan. Esto significa que, en determinados casos, la memoria caché de un segmento obtenido anteriormente se puede reutilizar, lo que mejora aún más el rendimiento.

TIP

En las rutas que comparten un layout, este se conservará cuando un usuario navegue entre páginas hermanas.

page.jsx

Cree su primera página agregando un archivo page.jsx dentro del directorio app:

ejemplo páginas en next js

src\app\dashboard\page.jsx

jsx
const Dashboard = () => {
  return (
    <div>
      <h1 className="text-center my-4 text-2xl">Dashboard</h1>
    </div>
  );
};
export default Dashboard;

src\app\dashboard\settings\page.jsx

jsx
const Settings = () => {
  return (
    <div>
      <h1 className="text-center my-4 text-2xl">Settings</h1>
    </div>
  );
};
export default Settings;

src\app\about\page.jsx

jsx
const About = () => {
  return (
    <div>
      <h1 className="text-center my-4 text-2xl">About</h1>
    </div>
  );
};
export default About;

layout.jsx

Un layout es un diseño que se compartirá entre las páginas de su aplicación. Puede crear un layout agregando un archivo layout.jsx dentro del directorio app:

Root layout

  • Este layout es obligatorio y se utiliza para envolver todas las páginas de su aplicación.
  • Este layout le permite modificar el HTML inicial devuelto por el servidor.
  • Los root layout deben contener etiquetas <html> y <body>.
  • Los layout son componentes de servidor de forma predeterminada, pero se pueden configurar como un componente de cliente.
  • Los layouts pueden obtener datos.
  • No es posible pasar datos entre un layout padre y sus hijos. Sin embargo, puede obtener los mismos datos en una ruta más de una vez y React eliminará automáticamente las solicitudes sin afectar el rendimiento.
  • Un archivo layout.js y page.js se pueden definir en la misma carpeta. El layout envolverá la página.

src\app\layout.jsx

jsx
import "./globals.css";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <p className="ml-5">RootLayout</p>
        {children}
      </body>
    </html>
  );
}

Layout anidados

ejemplo layout dashboard

src\app\dashboard\layout.jsx

jsx
const DashboardLayout = ({ children }) => {
  return (
    <div>
      <p className="ml-10">DashboardLayout</p>
      {children}
    </div>
  );
};
export default DashboardLayout;

template.jsx

Las template son similares a los layout en que envuelven cada diseño o página secundaria. A diferencia de los layout que persisten en las rutas y mantienen el estado, las template crean una nueva instancia para cada uno de sus elementos secundarios en la navegación. Esto significa que cuando un usuario navega entre rutas que comparten una plantilla, se monta una nueva instancia del componente, los elementos DOM se recrean, el estado no se conserva y los efectos se vuelven a sincronizar.

ejemplo template next

Puede haber casos en los que necesite esos comportamientos específicos, y las template serían una opción más adecuada que los layout. Por ejemplo:

  • Entrar/salir de animaciones usando CSS o bibliotecas de animación.
  • Funciones que se basan en useEffect(p. ej., registro de visitas a la página) y useState(p. ej., un formulario de comentarios por página).
  • Para cambiar el comportamiento del marco predeterminado. Por ejemplo, los límites de suspenso dentro de los layouts solo muestran el respaldo la primera vez que se carga el layout y no al cambiar de página. Para las template, el respaldo se muestra en cada navegación.

WARNING

Recomendamos usar layout a menos que tenga una razón específica para usar template.

Grupos de rutas

  • Para organizar rutas sin afectar la URL, cree un grupo para mantener juntas las rutas relacionadas.
  • La denominación de los grupos de rutas no tiene un significado especial más que para la organización. No afectan la ruta de la URL.

grupos de rutas next

Esto es muy últil para compartir un mismo layout o template.

ejemplo compartir layout

src\app(marketing)\layout.jsx

jsx
const MarketingLayout = ({ children }) => {
  return (
    <>
      <p className="ml-8">MarketingLayout</p>
      {children}
    </>
  );
};
export default MarketingLayout;

Múltimples root layout

  • Para crear varios root layout, elimine el archivo de nivel superior src\app\layout.jsx y agregue un layout.js archivo dentro de cada grupo de rutas.

WARNING

Navegar a través de múltiples root layout provocará una carga completa de la página

ejemplo multiples root layout

src\app(admin)\layout.jsx

jsx
import "../globals.css";

export const metadata = {
  title: "RootLayout Admin",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="es">
      <body>
        <p className="ml-5">RootLayout Admin</p>
        {children}
      </body>
    </html>
  );
}

src\app(marketing)\layout.jsx

jsx
import "../globals.css";

export const metadata = {
  title: "RootLayout Marketing",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="es">
      <body>
        <p className="ml-5">RootLayout Marketing</p>
        {children}
      </body>
    </html>
  );
}

¿Cual sería el home?

En este caso la página raiz debería estar en algún grupo de ruta para que no se destruya el sitio web.

Parámetros de ruta

Cuando no se conoce los nombres exactos de las rutas, se puede usar parámetros de ruta para crear rutas dinámicas.

Compilación o tiempo de ejecución

Una de las características de Next es que podemos elegir si queremos que la ruta se compile (cuando construimos nuestro sitio web) o se ejecute en tiempo de ejecución.

Generating static params

Se puede crear un segmento dinámico envolviendo el nombre de una carpeta entre corchetes: [folderName]. Por ejemplo, [id] o [slug].

src\app(marketing)\blog[slug]\page.jsx

jsx
const Slug = ({ params }) => {
  console.log(params);
  return <div className="text-center my-5 text-3xl text-indigo-500">Slug</div>;
};
export default Slug;
RutaURL de ejemploparams
app/blog/[slug]/page.js/blog/a{ slug: 'a' }
app/blog/[slug]/page.js/blog/b{ slug: 'b' }
app/blog/[slug]/page.js/blog/c{ slug: 'c' }

Catch-all Segments

Los parámetros opcionales se pueden hacer agregando tres puntos (...) al principio del nombre del segmento. Por ejemplo, [...id].

src\app\(marketing)\shop\[...id]\page.jsx

jsx
const Id = ({ params }) => {
  console.log(params);
  return <div className="text-center my-5 text-3xl text-indigo-500">Id:</div>;
};
export default Id;

Esto conincidirá con /shop/1, /shop/tablet/hp, /shop/tablet/hp/1 y así sucesivamente.

Optional Catch-all Segments

La diferencia entre los parámetros catch-all y catch-all opcional es que, la ruta sin el parámetro también coincide.

  • src\app\(marketing)\shop\[[...id]]\page.jsx
  • Elimina el archivo src\app(marketing)\shop\page.jsx y prueba la ruta: /shop

Linking and Navigating

El enrutador Next.js utiliza enrutamiento centrado en el servidor con navegación del lado del cliente. Esto significa que la página se renderiza en el servidor y se envía al cliente, pero cuando se navega a una nueva página, el cliente se encarga de la navegación.

Hay dos formas de navegar entre rutas:

  • Usando el componente <Link>
  • Usando el hook useRouter

Es un componente de react que amplia el elemento ancla.

jsx
import Link from "next/link";
jsx
<Link href="/blog">Blog Page</Link>

TIP

el href siempre debe comenzar con una barra / y apuntar a la ruta de la página raíz.

params

src\app(marketing)\blog\page.jsx

jsx
import TitlePage from "@/components/pagesComponets/TitlePage";
import Link from "next/link";

export const POST = [
  {
    id: 1,
    title: "Post 1",
    slug: "post-1",
    content:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.",
  },
  {
    id: 2,
    title: "Post 2",
    slug: "post-2",
    content:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.",
  },
  {
    id: 3,
    title: "Post 3",
    slug: "post-3",
    content:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum.",
  },
];

const Blog = () => {
  return (
    <>
      <TitlePage title="Blog" />

      <main className="container mx-auto grid gap-5">
        {POST.map((post) => (
          <div
            key={post.id}
            className="p-5 rounded-md shadow"
          >
            <h2 className="text-xl font-bold mb-5">{post.title}</h2>
            <Link
              href={`/blog/${post.slug}`}
              className="bg-indigo-800 text-white px-4 py-2 rounded hover:bg-indigo-950 transition-all duration-300"
            >
              Más información ...
            </Link>
          </div>
        ))}
      </main>
    </>
  );
};
export default Blog;

useRouter

useRouter (useRouter API) le permite cambiar de ruta programáticamente. Pero solo en client components.

Client components

Para utilizar un componente del lado del cliente, solo debe agregar "use cliente" al inicio de documento.

Es importante que esté en la primera línea del archivo, de lo contrario, no funcionará.

src\app(marketing)\blog[slug]\page.jsx

jsx
"use client";

import TitlePage from "@/components/pagesComponets/TitlePage";
import { POST } from "../page";
import { useRouter } from "next/navigation";

const Slug = ({ params }) => {
  const post = POST.find((post) => post.slug === params.slug);

  const router = useRouter();
  const handleClickBack = () => {
    // router.back();
    router.push("/blog");
  };

  return (
    <>
      <TitlePage title="Slug" />
      <main className="container mx-auto">
        <h1 className="text-3xl font-bold mb-5">{post.title}</h1>
        <p className="text-gray-700 mb-5">{post.content}</p>
        <button
          onClick={handleClickBack}
          className="bg-indigo-800 text-white px-4 py-2 rounded hover:bg-indigo-950 transition-all duration-300"
        >
          Regresar
        </button>
      </main>
    </>
  );
};
export default Slug;

TIP

Para este último caso recomendaría utilizar Link, ya que es más fácil de mantener y de leer. Además cuando consumamos datos podemos seguir utilizando esta página como server components.

Almacenamiento en caché

El nuevo enrutador tiene una memoria caché que almacena el resultado renderizado de los componentes del servidor.

Esto significa que, en ciertos casos, el enrutador puede reutilizar el caché en lugar de realizar una nueva solicitud al servidor. Esto mejora el rendimiento al evitar volver a obtener datos y volver a renderizar componentes innecesariamente.

El enrutador evita el trabajo innecesario al reutilizar segmentos que no han cambiado (por ejemplo, layout compartidos). Esto también se conoce como representación parcial.

ejemplo representación parcial

Prefetching

El prefetching es una forma de precargar una ruta en segundo plano antes de que se visite. El resultado renderizado de las rutas precargadas se agrega a la memoria caché del lado del cliente. Esto hace que la navegación a una ruta precargada sea casi instantánea.

De forma predeterminada, las rutas se obtienen previamente a medida que se vuelven visibles en la ventana gráfica cuando se usa el componente <Link> o useRouter (con prefetch). Esto puede suceder cuando la página se carga por primera vez o al desplazarse.

  • Prefetching solo está habilitada en producción.
  • Prefetching se puede deshabilitar pasando prefetch={false} a <Link>.

soft vs hard navigation

  • hard navigation: la memoria caché se invalida y el servidor recupera los datos y vuelve a representar los segmentos modificados.
  • soft navigation: la memoria caché para los segmentos modificados se reutiliza (si existe) y no se realizan nuevas solicitudes de datos al servidor.

Condiciones para soft navigation

  • Next.js usará la navegación suave si la ruta a la que navega ha sido precargada y no incluye segmentos dinámicos o tiene los mismos parámetros dinámicos que la ruta actual.
    • Navegar de /dashboard/team-red/* a /dashboard/team-red/* será una navegación suave.
    • Navegar de /dashboard/team-red/* a /dashboard/team-blue/* será una navegación difícil.
  • Back/Forward Navigation: La navegación hacia atrás y hacia adelante tiene un comportamiento de navegación suave.

TIP

Puedes fijarte en el título de la pestaña del navegador para saber si la navegación fue soft o hard. Si ves un spinner de carga, significa que la navegación fue hard.

Intenta agregar este link a la página de blog:

jsx
<div className="text-center">
  <Link href="/perfil">ir a perfil</Link>
</div>

¿Es una navegación hard o soft?

SEO y metadatos

jsx
import TitlePage from "@/components/pagesComponets/TitlePage";

export const metadata = {
  title: "Perfil",
  description: "perfil - Generated by create next app",
};

const Perfil = () => {
  return (
    <>
      <TitlePage title="Perfil" />
    </>
  );
};
export default Perfil;

generateMetadata

jsx
import TitlePage from "@/components/pagesComponets/TitlePage";
import { POST } from "../page";
import Link from "next/link";

// en este caso el async podría omitirse
// pero en la mayoría de los casos se necesitará
export async function generateMetadata({ params }) {
  const post = POST.find((post) => post.slug === params.slug);
  return {
    title: post.title,
    description: post.title + " - Descripción",
  };
}

const Slug = ({ params }) => {
  const post = POST.find((post) => post.slug === params.slug);

  return (
    <>
      <TitlePage title="Slug" />
      <main className="container mx-auto">
        <h1 className="text-3xl font-bold mb-5">{post.title}</h1>
        <p className="text-gray-700 mb-5">{post.content}</p>
        <Link
          href="/blog"
          className="bg-indigo-800 text-white px-4 py-2 rounded hover:bg-indigo-950 transition-all duration-300"
        >
          Regresar
        </Link>
      </main>
    </>
  );
};
export default Slug;