Next.js 16.3 Instant Navigations: cero esperas al navegar

Next.js 16.3 Instant Navigations — Dominicode

Written by

in

,

Llevaba semanas con una queja recurrente de un cliente. Su app en Next.js con App Router se sentía lenta. No el server, no la base de datos — las navegaciones. Hacías clic en un enlace y durante un segundo entero no pasaba nada. Literalmente nada. Next.js 16.3 Instant Navigations es la respuesta directa a ese problema.

En un modelo server-driven, cada navegación implica un roundtrip de red. El cliente espera, el servidor procesa, responde, aparece la página. En una SPA ese segundo no existe — el cliente muestra una shell inmediata mientras los datos llegan. Next.js había apostado por el servidor, pero el coste en percepción de velocidad era real.

Nota de versión: Next.js 16.3 está actualmente en preview (instalable con npm install next@preview). Las APIs que describo aquí son las publicadas el 25 de junio de 2026 en el blog oficial de Next.js. Pueden cambiar antes del release estable.


El problema que 16.3 viene a resolver

En Next.js clásico con Server Components, el flujo de navegación era este:

  1. El usuario hace clic en un enlace.
  2. El navegador no hace nada visible.
  3. El servidor procesa la ruta, genera el HTML, responde.
  4. La página aparece.

Para apps orientadas a contenido — un blog, un periódico — esto funciona. Para dashboards, herramientas internas, apps tipo SaaS, ese segundo de nada destruye la experiencia.

Las SPAs resuelven esto de otra forma: descargan el código de cada ruta de antemano y muestran una shell inmediata mientras los datos llegan. Sensación instantánea. Next.js tenía el prefetching, pero lo hacía a nivel de link individual, lo que generaba decenas de peticiones al servidor cada vez que el usuario hacía scroll por una lista de enlaces.

16.3 cambia los dos flancos del problema.


Cómo funciona Instant Navigations

Paso 1: habilitar Cache Components

Todo empieza con un flag en next.config.ts:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
};

export default nextConfig;

Este flag activa el modelo de Cache Components — el nuevo paradigma de Next.js donde el caching es explícito con 'use cache' en lugar de implícito y confuso como en versiones anteriores. Con él habilitado, Next.js puede generar un "shell" para cada ruta: la parte de la UI que puede renderizarse sin esperar al servidor.

Paso 2: elegir el modo de cada ruta — Stream, Cache o Block

Con Cache Components activo, cuando una ruta hace await a datos del servidor, Next.js en desarrollo te muestra un panel llamado Instant Insights. Este panel detecta qué rutas están bloqueando la navegación y te da tres opciones:

Stream con <Suspense>

La ruta muestra inmediatamente una shell con estados de carga, y los datos se van incluyendo por streaming conforme llegan:

// app/products/[id]/page.tsx
import { Suspense } from 'react';
import { ProductDetail } from './product-detail';
import { ProductSkeleton } from './product-skeleton';

export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  return (
    <div>
      <h1>Producto</h1>
      <Suspense fallback={<ProductSkeleton />}>
        <ProductDetail id={id} />
      </Suspense>
    </div>
  );
}

La navegación es inmediata. El usuario ve la shell con el skeleton. Los datos llegan después. Sensación de SPA.

Cache con 'use cache'

Si la ruta depende de datos que se pueden cachear, marcas la función con 'use cache' y Next.js sirve el resultado cacheado de forma instantánea en navegaciones posteriores:

// app/dashboard/analytics/page.tsx
import { unstable_cacheLife as cacheLife } from 'next/cache';
import { Suspense } from 'react';

async function AnalyticsSection() {
  'use cache';
  cacheLife('minutes'); // TTL de caché explícito
  const data = await fetchAnalytics();
  return <Chart data={data} />;
}

export default function AnalyticsPage() {
  return (
    <Suspense fallback={<AnalyticsSkeleton />}>
      <AnalyticsSection />
    </Suspense>
  );
}

El usuario ve el contenido cacheado de forma inmediata. Si el cache está fresco, la experiencia es idéntica a una SPA.

Block: cuando quieres que la navegación espere al servidor

Hay casos donde no quieres mostrar una shell. Un blog no debería mostrar un spinner donde va el artículo — o muestras el artículo o no navegas. Para esos casos, exportas instant = false:

// app/blog/[slug]/page.tsx
export const instant = false; // Esta ruta bloquea hasta tener respuesta del servidor

export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);
  return <Article post={post} />;
}

Next.js deja de reportar esta ruta como problema de rendimiento. Has decidido conscientemente que prefieres la espera a mostrar una UI incompleta. La diferencia es que ahora es una decisión explícita, no un comportamiento por defecto que no entiendes.


Partial Prefetching: prefetchear smarter, no harder

El segundo gran cambio es cómo Next.js hace prefetching.

En 16.2, si tenías una lista de veinte enlaces a /chat/[id], Next.js enviaba veinte peticiones de prefetch al servidor — una por link visible en el viewport. Ineficiente y costoso.

En 16.3, con Partial Prefetching habilitado, Next.js prefetchea un shell por ruta, no por link. Veinte links a /chat/[id] generan exactamente una petición: la del shell de /chat/[id]. Ese shell se cachea en el cliente durante toda la sesión.

Para habilitarlo:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

export default nextConfig;

Prefetching por link cuando necesitas más

El Partial Prefetching es conservador por diseño — solo prefetchea el shell. Si quieres que un link concreto prefetchee también contenido específico, añades prefetch={true} al componente <Link>:

// Una lista donde quieres que el header del chat se vea instantáneamente
export function ChatList({ chats }: { chats: Chat[] }) {
  return (
    <ul>
      {chats.map(chat => (
        <li key={chat.id}>
          {/* Prefetch completo para este link */}
          <Link href={`/chat/${chat.id}`} prefetch={true}>
            {chat.title}
          </Link>
        </li>
      ))}
    </ul>
  );
}

Y si quieres que el prefetch incluya contenido dinámico de request-time (no solo build-time), lo permites explícitamente en la ruta:

// app/chat/[id]/page.tsx
export const prefetch = 'allow-runtime';

La ventaja respecto al comportamiento anterior: ya no es todo o nada. Tienes granularidad real.


Navigation Inspector: ve el shell antes de que el usuario llegue

El Navigation Inspector es una herramienta de las Next.js DevTools que pausa cualquier navegación en el momento exacto del shell — antes de que lleguen los datos del servidor — mostrando visualmente qué partes de la ruta son instantáneas y cuáles requieren una petición de red.

En la práctica: haces clic en un enlace, el inspector lo detiene en el shell, ves el mapa completo de tu ruta. Cuando haces clic en "Resume", la navegación completa. Especialmente útil para identificar componentes que bloquean la navegación porque hacen await sin <Suspense> ni 'use cache'.


Testing: el helper instant() para Playwright

Para que las mejoras de rendimiento no retrocedan con refactorizaciones futuras, Next.js 16.3 incluye un test helper para Playwright:

// tests/navigation.spec.ts
import { expect, test } from '@playwright/test';
import { instant } from '@next/playwright';

test('el header del producto aparece sin esperar al servidor', async ({ page }) => {
  await page.goto('/products/shoes');

  // Todo lo que esté dentro de este bloque debe ser visible SIN red
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]');
    await expect(page.locator('h1')).toContainText('Baseball Cap');
    await expect(page.getByText('Checking inventory...')).toBeVisible();
  });

  // Esto sí puede esperar al servidor
  await expect(page.getByText('12 in stock')).toBeVisible();
});

instant() es el equivalente a un test de performance integrado en tu suite de e2e. Si un refactor convierte una ruta Stream en una ruta bloqueante, el test falla. Sin sorpresas en producción.

Si ya tienes tests de Angular y quieres aplicar la misma mentalidad a tus proyectos — testear comportamiento, no implementación — el curso de Testing en Angular te da esa base de forma sólida con Jest y Testing Library.


Comparativa: antes vs. después

Aspecto Next.js 16.2 Next.js 16.3
Comportamiento por defecto Bloquea hasta respuesta del servidor Stream o Cache para navegación inmediata
Prefetching 1 petición por link en viewport 1 shell por ruta, reutilizado entre links
Control por ruta No hay export const instant = false para rutas bloqueantes
Herramienta de diagnóstico Ninguna Instant Insights + Navigation Inspector
Testing de regresiones Manual instant() helper para Playwright
Configuración Implícita y confusa Explícita con cacheComponents y partialPrefetching

Cómo empezar hoy mismo

Para probar Instant Navigations en un proyecto existente:

  1. Instala el preview:

    npm install next@preview
    
  2. Habilita los flags en next.config.ts:

    const nextConfig: NextConfig = {
      cacheComponents: true,
      partialPrefetching: true,
    };
    
  3. Arranca el servidor de desarrollo. Verás el panel Instant Insights con las rutas que están bloqueando la navegación.

  4. Identifica, decide y verifica. Para cada ruta bloqueante, elige Stream, Cache o Block. El Navigation Inspector confirma que el shell funciona antes de ir a producción.

El equipo de Vercel lo validó en v0 — su propia app — antes del release. Los tiempos de navegación bajaron significativamente en las rutas que adoptaron el nuevo modelo.

Si quieres ver cómo se integra este tipo de arquitectura con IA y streaming en tiempo real, en Dominicode Labs estamos construyendo proyectos que combinan Next.js con streaming de LLMs — exactamente el tipo de apps donde Instant Navigations marca la diferencia más visible. Para entender la Claude API con TypeScript antes de integrarla, este crash course es el punto de partida.


Casos de uso donde esto cambia más

Dashboards con datos en tiempo real. Cada cambio de sección era un segundo de espera. Con Stream + Suspense, el layout del dashboard aparece inmediatamente y los datos llegan después.

Apps de chat o mensajería. Con Partial Prefetching, navegar entre conversaciones — aunque sean decenas — genera una sola petición de prefetch por ruta, no una por cada enlace visible.

E-commerce. Las páginas de producto pueden mostrar la estructura (imagen placeholder, nombre, botón "Añadir al carrito") de forma instantánea mientras el inventario y el precio se cargan.

Herramientas internas con muchas secciones. El menú lateral con 30 links ya no genera 30 peticiones de prefetch al cargar la página.

Si trabajas con Astro para partes estáticas de tu sitio y Next.js para las dinámicas, el análisis de Astro v7 te ayuda a decidir qué encaja en cada capa.


FAQ

¿Instant Navigations funciona con el Pages Router o solo con App Router?

Solo con App Router. Cache Components y el modelo de shells requieren Server Components, que no existen en Pages Router. Si aún tienes un proyecto en Pages Router, esta es una razón más para evaluar la migración.

¿cacheComponents: true cambia el comportamiento de caching de mis datos?

Sí, de forma intencional. El nuevo modelo hace el caching explícito: nada se cachea por defecto a menos que uses 'use cache'. Si venías de fetch con opciones implícitas de cache, tendrás que revisar tu estrategia de datos. Es un cambio de paradigma, no solo un flag de navegación.

¿El Partial Prefetching incrementa el coste de servidor?

Al contrario. En 16.2, con veinte links en pantalla tenías veinte peticiones de prefetch. En 16.3, con Partial Prefetching, tienes una petición por ruta distinta. En un escenario real con listas de items que apuntan al mismo route pattern, la reducción de peticiones puede ser del 90%.

¿Puedo usar <Link prefetch={true}> para todo y obtener el comportamiento anterior?

Técnicamente sí, pero estarías ignorando el punto. El comportamiento anterior era ineficiente. <Link prefetch={true}> existe para casos específicos donde necesitas prefetchear más que el shell en un link concreto — no como reemplazo global del viejo modelo.

¿Cuándo sale el release estable de Next.js 16.3?

No hay fecha oficial confirmada. El equipo indica que están resolviendo issues conocidos (algunos casos con Safari en Instant Insights, y rutas bloqueantes que no se reportan correctamente con Partial Prefetching activo). La recomendación es probar el preview en proyectos de desarrollo, no en producción.

¿Puedo escribir tests con instant() antes del release estable?

Sí. El paquete @next/playwright ya incluye el helper y es seguro usarlo en tu suite de e2e. Si el release estable cambia el comportamiento, los tests te lo dirán antes de que llegue a producción.


Next.js 16.3 no reinventa el framework. Lo que hace es cerrar la brecha más molesta que tenían los Server Components: la sensación de lentitud al navegar. Stream, Cache y Block son tres palabras, pero detrás hay un modelo de pensamiento claro sobre qué parte de tu UI puede ser instantánea y cuál no.

La clave no está en activar los flags y esperar magia. Está en recorrer tus rutas con el Navigation Inspector, entender qué está bloqueando, y tomar la decisión correcta para cada una.

Si tu próximo proyecto combina Next.js con agentes de IA o herramientas de Claude Code, en el curso Construye con IA vemos exactamente cómo estructurar apps que necesitan streaming, caché inteligente y navegación fluida desde el primer día.


Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *