← Back to list

Next.js — Componente Link

¿Qué es Link? Características Ventajas ¿Cómo se crea o implementa? ¿Cómo funciona? Opciones disponibles Ejemplos A considerar ¿Es…

Mauricio Garcia · 2025-05-26 21:25 · 0 claps · 6.8 min read paywalled
#nextjs #links #href #app-router
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Next.js — Componente Link

Introducción

  • ¿Qué es Link?
  • Características
  • Ventajas
  • ¿Cómo se crea o implementa?
  • ¿Cómo funciona?
  • Opciones disponibles
  • Ejemplos
  • A considerar
  • ¿Es compatible usar Link dentro de Layouts?
  • Comparación con <a> tradicional

Lee el artículo gratis → friends link o en Github

Todos los ejemplos los podrás encontrar en el repositorio next.js-15.3–1[ref] Acá puedes ver todas las stories de next.js [ref]

Componente Link

En el ecosistema de Next.js, el componente Link cumple un rol fundamental en la experiencia de usuario: permite transiciones entre páginas sin recargar el navegador, manteniendo el estado del cliente y optimizando el rendimiento mediante precarga automática de recursos.

:: ¿Qué es Link?

Link es un componente de alto nivel proporcionado por Next.js para manejar navegación interna en aplicaciones web. Está diseñado para reemplazar el uso de <a href="..."> en contextos donde se navega entre rutas definidas en el App Router o Pages Router.

Este componente mejora la navegación de una aplicación tipo SPA (Single Page Application) sin perder las ventajas de SSR (Server-Side Rendering) o SSG (Static Site Generation).

:: Características

  • Navegación sin recarga: transiciones instantáneas sin refrescar el documento completo.
  • Precarga automática: Next.js precarga JavaScript y datos asociados en segundo plano.
  • Detección por visibilidad: usa IntersectionObserver para prefetch inteligente.
  • Comportamiento de <a> respetado: clic derecho, nueva pestaña, accesibilidad, SEO.
  • Soporte para rutas dinámicas: compatible con [foldername],[slug], [...slug], [[...slug]].

:: Ventajas

  • Mejora el rendimiento al evitar recargar el navegador.
  • Anticipa la navegación del usuario con prefetch.
  • Compatible con SSR/SSG, pero con experiencia SPA.
  • Soporta objetos href para query params dinámicos.
  • Puede envolver otros elementos (<span>, <div>) sin perder funcionalidad.

:: ¿Cómo se crea o implementa?

Primero, importa el componente:

import Link from 'next/link';

Su uso más básico:

<Link href="/movies">Películas</Link>

También puedes incluir un <a> explícito si necesitas más control:

<Link href="/movies">
  <a className="text-blue-500 hover:underline">Películas</a>
</Link>

Con elementos personalizados:

<Link href="/movies">
  <span className="text-blue-500 underline">Películas</span>
</Link>

Con parámetros dinámicos:

<Link href={`/blog/${slug}`}>{title}</Link>

:: ¿Cómo funciona?

Link utiliza el sistema interno de navegación client-side basado en next/navigation (en App Router) o next/router (en Pages Router). El flujo es:

  • Detecta si el destino (href) es interno.
  • Si es interno, intercepta el clic y evita la recarga completa.
  • Usa pushState o replaceState para modificar el historial.
  • Precarga la página de destino si está visible.
  • Hidrata el nuevo contenido sin perder estado global.

:: Opciones disponibles

**herf (Obligatorio)**

La ruta interna o URL a la que navegar.

<Link href="/movies">Películas</Link>

También permite pasar un objeto con las propiedades pathname y query para construir la URL:

// Navega a /movies?name=test
<Link
  href={{
    pathname: '/movies',
    query: { name: 'test' },
  }}
>
  Películas con query
</Link>

**Replace (Default false)**

Reemplaza la entrada actual en el historial ( history.replace)

<Link href="/movies" replace>Movies</Link>

**prefetch (Default null)**

Inicia una precarga del recurso cuando un componente <Link> se visualiza en la ventana del usuario. Next.js precarga y carga la ruta vinculada y sus datos en segundo plano, con la finalidad de mejorar el rendimiento de las navegaciones del lado del cliente.

Valores que acepta la propiedad:

  • null — Para las rutas estáticas, se precargará la ruta completa (incluyendo datos). Para las rutas dinámicas, se precargará la ruta parcial.
  • true — Para rutas estáticas y dinámicas, se precargará la ruta completa (incluyendo datos).
  • false — La precarga nunca se producirá.
<Link href="/movies" prefetch={false}>Movies</Link>

**scroll (Default true)**

El comportamiento predeterminado de Link es mantener la posición del scroll (de forma similar a como los navegadores se manejan).

  • true— Cuando se navega a una nueva página, la posición del scroll se mantendrá siempre que la página sea visible en la ventana. En caso contrario, se desplazará hasta la parte superior del primer elemento de la página.
  • false— Next.js no intentará desplazarse hasta el primero elemento de la página.
<Link href="/movies" scroll={false}>Movies</Link>

Los links tienen scroll = {false}

Los links tienen scroll = {false}

También permite desplazarse a un elemento específico de la página utilizando un enlace con hash (#) hacia un id determinado:

<Link href="/tv#popular">TV popular</Link>

**onNavigate**

Cuando se da clic, se manda a llamar un manejador de eventos (event handler) de lado del cliente. El manejador recibe un objeto que incluye el método preventDefault().

Importante: onNavigate solo se ejecuta durante navegaciones del lado del cliente y dentro del mismo origen. No se activará si el usuario utiliza teclas modificadoras (como Ctrl/Cmd + clic) o si la navegación apunta a una URL externa o los enlaces con el atributo download, ya que este comportamiento está limitado exclusivamente a transiciones internas dentro de la aplicación.

<Link
  href="/movies"
  onNavigate={(e) => {
    // Sólo se ejecuta durante la navegación SPA
    console.log('Navegando...')

    // Opcionalmente impedir la navegación
    // e.preventDefault()
  }}
>
  Movies
</Link>

:: Ejemplos

Ejemplo — Navegación entre páginas estáticas

Nota: Al ser ejemplos básicos no he agregado la navegación dentro del layout.tsx, pero como buena práctica deberías de hacerlo.

//src/app/page.tsx

import Link from 'next/link';

export default function Home() {
  return (
    <main className="flex flex-col min-h-screen">
      <nav className="bg-gray-700 text-white p-2">
        <div className="flex justify-between items-center px-4 py-3">
          <div className="flex space-x-4">
            <Link href="/" className="hover:text-gray-300">
              Inicio
            </Link>
            <Link href="/movies" className="hover:text-gray-300">
              Películas
            </Link>
            <Link href="/tv" className="hover:text-gray-300">
              Series
            </Link>
          </div>
        </div>
      </nav>
    </main>
  );
}
// src/app/movies/page.tsx

import Link from 'next/link';

export default function Movies() {
  return (
    <div className="flex flex-col min-h-screen">
      <nav className="bg-gray-700 text-white p-2">
        <div className="flex justify-between items-center px-4 py-3">
          <div className="flex space-x-4">
            <Link href="/" className="hover:text-gray-300">
              Inicio
            </Link>
          </div>
        </div>
      </nav>
      <h2 className="text-xl font-semibold p-2">
        Bienvenido a la sección de Películas
      </h2>
    </div>
  );
}
// src/app/series/page.tsx
import Link from 'next/link';

export default function Series() {
  return (
    <div className="flex flex-col min-h-screen">
      <nav className="bg-gray-700 text-white p-2">
        <div className="flex justify-between items-center px-4 py-3">
          <div className="flex space-x-4">
            <Link href="/" className="hover:text-gray-300">
              Inicio
            </Link>
          </div>
        </div>
      </nav>
      <h2 className="text-xl font-semibold p-2">
        Bienvenido a la sección de Series
      </h2>
    </div>
  );
}

Al iniciar el servidor (npm run dev), acceder a http://localhost:3000:

Descarga el proyecto actual [ref] (branch: link)

Ejemplo — Navegación entre páginas dinámicas

//src/app/page.tsx

import Link from 'next/link';

export default function Home() {
  return (
    <main className="flex flex-col min-h-screen">
      <nav className="bg-gray-700 text-white p-2">
        <div className="flex justify-between items-center px-4 py-3">
          <div className="flex space-x-4">
            <Link href="/" className="hover:text-gray-300">
              Inicio
            </Link>
            <Link href="/movies" className="hover:text-gray-300">
              Películas
            </Link>
            <Link href="/series" className="hover:text-gray-300">
              Series
            </Link>
          </div>
        </div>
        <div className="bg-gray-500 px-4 py-2">
          <div className="flex space-x-4">
            <Link href="/media/movies/action" className="hover:text-gray-300">
              Acción
            </Link>
            <Link href="/media/movies/comedy" className="hover:text-gray-300">
              Comedia
            </Link>
            <Link href="/media/movies/drama" className="hover:text-gray-300">
              Drama
            </Link>
            <Link href="/media/series/anime" className="hover:text-gray-300">
              Anime
            </Link>
            <Link
              href="/media/series/documentary"
              className="hover:text-gray-300"
            >
              Documentales
            </Link>
          </div>
        </div>
      </nav>
    </main>
  );
}
//src/app/media/[...slug]/page.tsx

import Link from 'next/link';

export default async function MediaPage({
  params,
}: {
  params: Promise<{ slug: string[] }>;
}) {
  const { slug } = await params;
  return (
    <div className="flex flex-col min-h-screen">
      <nav className="bg-gray-700 text-white p-2">
        <div className="flex justify-between items-center px-4 py-3">
          <div className="flex space-x-4">
            <Link href="/" className="hover:text-gray-300">
              Inicio
            </Link>
          </div>
        </div>
      </nav>
      <h2 className="text-xl font-semibold p-2">Por categoría</h2>
      <p>Ruta actual: /media/{slug.join('/')}</p>
      <pre className="mt-4 bg-amber-100 text-amber-700 p-2 rounded">
        {JSON.stringify(slug, null, 2)}
      </pre>
    </div>
  );
}

Al iniciar el servidor (npm run dev), acceder a http://localhost:3000:

Descarga el proyecto actual [ref] (branch: link)

:: A considerar

  • No usar Link para URLs externas. Usa <a href="https://..."> directamente.
  • Evita Link sin href. Genera errores de navegación.
  • Evita usarlo en archivos comoroute.ts olayout.tsx.
  • Asegura accesibilidad: usa elementos interactivos como <span> o <button>.
  • Usa replace si no deseas guardar en historial.
  • El prefetch solo funciona en producción.

:: ¿Es compatible usar Link dentro de Layouts?

Si, pero evítalos usar cuando:

  • **layout.tsx depende de parámetros (params) — Si estás en un layout dinámico como app/media/[...slug]/layout.tsx, los params cambian cuando navegas entre subrutas, pero el layout no se recarga automáticamente**. Eso puede provocar desincronización entre lo que muestra el layout y el contenido actual.
  • Precarga innecesaria en cada ruta — Si colocas muchos Link dentro de un layout persistente y esos links tienen prefetch activo (por defecto), se puede generar precarga innecesaria de muchas rutas cada vez que se monta el layout. Esto no rompe nada, pero puede afectar el rendimiento en apps grandes.

Entonces:

  • Usa Link en layouts cuando el menú sea global y no dependa de params.
  • Si el menú cambia según la ruta, considera moverlo a un layout más específico o incluso al nivel de page.tsx.
  • Puedes encapsular la navegación en un componente tipo <Navbar /> y re-utilizarlo donde convenga.

:: Comparación con <a> tradicional

Hasta este punto, has aprendido a utilizar el componente Link para implementar navegación interna. Comprendiste cómo este componente mejora la experiencia del usuario al habilitar transiciones sin recarga, precarga automática de rutas visibles y soporte completo para rutas dinámicas como [slug] o [...slug].


메타데이터
post_id
d0b60a91e0b8
slug
next-js-componente-link-d0b60a91e0b8
url
https://medium.com/@mauriciogc/next-js-componente-link-d0b60a91e0b8
canonical_url
https://medium.com/@mauriciogc/next-js-componente-link-d0b60a91e0b8
author_url
https://medium.com/@mauriciogc
status
ok
fetched_at
2026-07-19 17:36:43