Blog Logo

Navegações instantâneas no Next.js com agentes de IA

Faz tempo que sabemos deixar um agente escrevendo código que passa nos testes, e até como montar um time de agentes de IA. O que a gente não sabia era fazer ele escrever código que parece rápido. “Deixa essa navegação instantânea” é um requisito difuso: não existe assertion de Jest para “isso aqui tá fluido”. Um agente pode refatorar às cegas, medir no olho e declarar vitória sem que nada tenha melhorado de verdade.

A Vercel acabou de contar como resolveram exatamente isso no v0, a plataforma de geração de apps deles: um agente em loop, armado com um teste determinístico e um Skill cheio de padrões provados, que derrubou os tempos de navegação em produção para perto de zero. E o interessante não é o case de marketing, é a peça técnica que torna o loop possível e que você já pode usar na sua app: o helper instant() que vem no Next.js 16.3.

Diagrama do loop agente + teste para navegações instantâneas

O problema: apps dinâmicas que “parecem site”

Numa app server-driven com React Server Components, navegar normalmente significa um roundtrip de rede: você clica, nada acontece, o servidor responde, a página aparece. Até agora o Next.js oferecia duas saídas: pré-renderizar estaticamente no build (impossível com dados personalizados por usuário) ou marcar os links com prefetch completo (caro, e com um request de prefetch por link — algo que o próprio time admite no post do Next.js 16.3 que ficava ridículo: vinte chats numa sidebar, vinte requests).

A solução da 16.3, chamada Instant Navigations, copia o truque das SPAs: em vez de prefetch por link, o Next.js faz prefetch de um shell reutilizável por rota, cacheado inteiro no navegador. O shell guarda tudo que pode aparecer sem esperar o servidor: layout, títulos, skeletons. No clique, o shell aparece na hora e o conteúdo dinâmico chega depois por streaming.

Para ativar são duas flags no next.config.ts:

import type { NextConfig } from 'next';

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

export default nextConfig;

Com cacheComponents ligado, quando uma rota faz await de dados no servidor você tem três opções — e essa é a parte conceitual que importa:

  • Stream com <Suspense>: o usuário vê o fallback na hora, e o conteúdo entra depois.
  • Cache com 'use cache': o usuário vê na hora uma UI cacheada de uma execução anterior.
  • Block: se você prefere que aquela rota espere o servidor (um post de blog que você nunca quer mostrar como skeleton), declara explicitamente com export const instant = false na página ou no layout.

Em desenvolvimento, o painel Instant Insights aponta automaticamente quais rotas não estão instantâneas, transformando a navegação lenta num erro visível em vez de uma sensação vaga.

A peça-chave: um teste que prova que é “rápido”

Aqui está o que muda o jogo para os agentes. O Next.js 16.3 inclui um helper para Playwright, instant(), importado de @next/playwright, que pausa a navegação e deixa você verificar quais partes da UI ficam visíveis sem nenhum request de rede:

import { expect, test } from '@playwright/test';
import { instant } from '@next/playwright';

test('navegar para chats é instantâneo', async ({ page }) => {
  await page.goto('/');

  await instant(page, async () => {
    await page.getByRole('link', { name: 'Chats' }).click();
    await expect(page.getByTestId('app-title')).toBeVisible();
  });
});

Se o conteúdo esperado dependia da rede, o teste falha. Repare no que acabou de acontecer: um requisito qualitativo (“tem que parecer instantâneo”) virou uma verificação binária e determinística. E verificações binárias e determinísticas são exatamente o combustível de que um loop agêntico precisa.

O loop que deixou o v0 instantâneo

O caso prático foi publicado em Making Navigations Instant in v0. O loop é o clássico de agentes bem feitos, com as três coisas que todo fluxo sério com agentes precisa: um objetivo verificável, guardrails com padrões provados —como os que você define em AGENTS.MD—, e feedback real de produção:

  1. Definir o objetivo como um teste que falha: o agente escreve um teste com instant() para uma navegação lenta específica (por exemplo, de / para /chats).
  2. Aplicar o fix usando os padrões do Skill: refatorar até o teste passar.
  3. Repetir se não passar: o teste é a condição de saída do loop.
  4. Commitar o código E o teste: o teste fica no CI como guarda contra regressões.

O Skill que contém os padrões se instala com:

npx skills add vercel/next.js --skill next-cache-components-optimizer

E depois é só dar ao agente um prompt simples tipo “Make the navigation from ’/’ to ‘/chats’ instant using the next-cache-components-optimizer skill”. Não subestime o papel do Skill: sem ele, o agente improvisa refactors; com ele, o agente aplica receitas verificadas para cada tipo de bloqueio (rotas paralelas, auth gates, o modo de falha do shell vazio, skeletons responsivos).

E como são os fixes? Na maioria dos casos, algo surpreendentemente modesto: descer o acesso a dados dinâmicos para baixo de um boundary de Suspense, para o resto da página entrar no shell:

// antes: o await bloqueia a página inteira
export default async function WorkspacePage() {
  const session = await getServerSession();
  const team = await fetchTeam(session);
  return <TeamSettings team={team} />;
}

// depois: o shell pinta na hora, os dados entram por streaming
export default function WorkspacePage() {
  return (
    <SettingsPageLayout>
      <SettingsHeader title="Workspace" />
      <Suspense fallback={<WorkspaceSkeleton />}>
        <WorkspaceContent />
      </Suspense>
    </SettingsPageLayout>
  );
}

async function WorkspaceContent() {
  const session = await getServerSession();
  const team = await fetchTeam(session);
  return <TeamSettings team={team} />;
}

Outros casos exigiram refactors maiores, como tirar uma dependência bloqueante do root layout e movê-la para os componentes que realmente a usam. Exatamente o tipo de mudança que mexe em várias features, que um humano adia por meses, e que um loop test-driven ataca sem drama.

O resultado: a home (logada e deslogada), a página de detalhe do chat e todas as subpáginas de settings passaram de bloqueantes para instantâneas, com 16 testes novos impedindo que qualquer mudança futura — humana ou feita por agente — as deixe lentas de novo. Esse último ponto é o que mais me agrada: se os seus agentes vão continuar mexendo no repo, você precisa de verificadores que imponham limites, e “não quebre as navegações instantâneas” acabou de virar um deles.

Como ativar navegações instantâneas no Next.js 16.3

A 16.3 saiu como preview em junho (anúncio oficial) e se instala pela tag preview do npm:

npm install next@preview

Se a sua app ainda não usa Cache Components, existe um segundo Skill, next-cache-components-adoption, que guia o agente pela migração. Os dois estão documentados no guia de AI agents com Next.js e na doc de Instant Navigations.

Minha sugestão de ordem: ative cacheComponents numa branch, veja o que o Instant Insights aponta em desenvolvimento, escreva (ou deixe o agente escrever) um teste com instant() para a sua navegação mais importante, e rode o loop só nela. Não migre metade da app de uma vez: uma navegação verde no CI vale mais que vinte promessas.

A lição de fundo vai além do Next.js. Os frameworks que vão vencer na era dos agentes não serão os que geram mais código, e sim os que exportam verificadores: primitivas que transformam qualidades difusas — velocidade percebida, acessibilidade, UX — em testes determinísticos. É a mesma ideia de transformar capturas de tela em testes com Claude Code e TestSprite; aqui o verificador é o instant() e a qualidade é a velocidade percebida.

Perguntas frequentes

Preciso usar o v0 ou a Vercel para isso?

Não. O helper instant(), as flags e os Skills fazem parte do Next.js open source. O caso do v0 é só a demonstração; o loop funciona em qualquer app Next.js 16.3 com Cache Components.

Isso é a mesma coisa que Partial Prerendering?

Não. O PPR pré-renderiza em build time; aqui o pré-render do shell acontece em runtime, enquanto o usuário navega, e é cacheado no navegador. O próprio time da Vercel faz questão da distinção: conseguir navegações instantâneas dá bem menos trabalho que uma migração para PPR estático.

E se eu quiser que uma rota continue server-bound?

Declare export const instant = false na página ou no layout, e o erro do Instant Insights some. Você decide quais rotas são forçadas a ser instantâneas e quais não.


O que você achou?

Deixe sua opinião, pergunta ou sugestão. Os comentários são sincronizados com GitHub Discussions .

Voltar ao blog