Next.js: Navegação: Link e API Programática

Última atualização: 2026-08-26

A navegação é como um sistema de metrô — existem linhas automatizadas (componentes Link), opções para controle manual (navegação programática), trens expressos para trajetos rápidos (soft navigation) e rotas de transferência (hard navigation).

1. O Que Você Vai Aprender



2. Uma História Real de um Desenvolvedor Júnior

(1) Ponto de Dor: Experiência de navegação ruim entre páginas

Charlie tem desenvolvido recentemente um site de e-commerce usando Next.js e descobriu que a experiência de navegação é ruim:

"Quando um usuário clica no link de um produto na página inicial, a página fica em branco por 1 segundo antes de carregar. Toda vez que o usuário clica em 'Voltar', a página inteira recarrega. Além disso, há um bug — se um usuário preenche metade de um formulário, clica acidentalmente no link de um produto e depois clica em 'Voltar', todos os dados do formulário são perdidos."

Perguntas específicas:

Problema Impacto Feedback do Usuário
Tela em branco ao trocar de página Navegação truncada "Demora muito para clicar em um link"
Perda de Dados do Formulário Usuário redigita os dados "Preenchi até a metade, mas quando voltei, sumiu"
Pré-carregamento não é inteligente Não pré-carrega páginas visitadas com frequência "A transição mais lenta é da página inicial para a de detalhes do produto"
Posição de Rolagem Reiniciada Volta ao topo após paginar na página de lista "Tenho que rolar para baixo toda vez"

(2) Soluções com a Navegação do Next.js

Componente Link com pré-busca automática + preservação de estado via Soft Navigation + scroll={false} para preservar a posição de rolagem.

TSX
// Navegação Otimizada na Página de Lista de Produtos
<Link
  href={`/products/${product.id}`}
  prefetch={true}
  scroll={false}
  className="block p-4 border rounded hover:shadow"
>
  {product.title}
</Link>

(3) Ganhos

Dimensão Antes da Otimização (Tag <a> Padrão) Após Otimização (Componente <Link>)
Velocidade de Carregamento da Página 1–2 segundos (tela em branco) Instantâneo (cache pré-buscado)
Persistência de Dados do Formulário Perdidos ao descarregar a página Preservados no Soft Nav
Posição de Rolagem Mantida Volta ao Topo Retenção Exata
Avaliação da Experiência do Usuário 3,2/5 4,8/5


(1) Uso Básico

100%
graph LR
    A[Componente Link] --> B[Navegação no Cliente<br/>Sem recarregamento completo da página]
    A --> C[Pré-busca Automática<br/>Links dentro da viewport]
    A --> D[Controle de Rolagem<br/>scroll={false}]
    A --> E[Substituição de Histórico<br/>replace]

    style A fill:#cce5ff
    style B fill:#d4edda

Saída:

TEXT 📖 Somente leitura
Diagrama da navegação no lado do cliente usando o componente Link.
TSX
// ============================================
// Uso Básico do Componente Link
// ============================================

import Link from "next/link";

export default function Navigation() {
  return (
    <nav className="flex gap-6 p-4 bg-white shadow-sm">
      {/* Navegação Básica */}
      <Link href="/" className="text-blue-600 hover:underline">
        Início
      </Link>

      {/* Roteamento Dinâmico */}
      <Link href="/products/42" className="text-blue-600 hover:underline">
        Produto 42
      </Link>

      {/* Com parâmetros de consulta */}
      <Link
        href="/products?category=electronics&sort=price"
        className="text-blue-600 hover:underline"
      >
        Eletrônicos
      </Link>

      {/* URL Completa */}
      <Link
        href="https://help.example.com"
        className="text-blue-600 hover:underline"
      >
        Central de Ajuda
      </Link>
    </nav>
  );
}

Saída:

TEXT 📖 Somente leitura
Renderiza: Links de navegação usando o componente Link do Next.js para roteamento no lado do cliente.

Saída:

TEXT 📖 Somente leitura
O navegador exibe 4 links azuis:
Início → / 
Produto 42 → /products/42
Eletrônicos → /products?category=electronics&sort=price
Central de Ajuda → https://help.example.com (Link Externo)

(2) prefetch

Por padrão, o componente Link pré-carrega links dentro da viewport. O comportamento de pré-carregamento difere entre o servidor e o cliente:

Ambiente prefetch={true} prefetch={false}
Renderização no servidor Pré-busca dados da página e payload RSC Não pré-busca
Página Estática Pré-carrega a Página Inteira Não Pré-carrega
Dentro da viewport Comportamento padrão Nunca pré-carrega

▶ Exemplo: Controlando o Comportamento de prefetch

Saída:

TEXT 📖 Somente leitura
A página renderiza conforme descrito acima, com a UI atualizando com base no comportamento descrito.
TSX
// ============================================
// Controlar o Comportamento de Prefetch do Link
// ============================================

import Link from "next/link";

export default function ProductList() {
  const products = [
    { id: 1, name: "Mouse Sem Fio" },
    { id: 2, name: "Teclado" },
    { id: 3, name: "Monitor" },
  ];

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-4">Produtos</h1>

      {/* prefetch padrão: pré-carrega automaticamente links dentro da viewport */}
      {products.map(p => (
        <Link
          key={p.id}
          href={`/products/${p.id}`}
          className="block p-4 border-b hover:bg-gray-50"
        >
          {p.name}
        </Link>
      ))}

      {/* Desativar prefetch: Adequado para páginas não usadas com frequência */}
      <Link
        href="/archive"
        prefetch={false}
        className="block mt-4 text-gray-500"
      >
        Ver Arquivo (produtos antigos)
      </Link>

      {/* Prefetch Forçado: Adequado para a próxima página que você está prestes a visitar */}
      <Link
        href="/checkout"
        prefetch={true}
        className="block mt-4 px-6 py-2 bg-blue-600 text-white text-center rounded"
      >
        Ir para o Checkout
      </Link>
    </div>
  );
}

Saída:

TEXT 📖 Somente leitura
Mapeia os produtos para renderizar a lista.
Conteúdo: Produtos

Saída:

TEXT 📖 Somente leitura
Após o carregamento da página, isso pode ser observado no console de rede:
1. /products/1, /products/2, /products/3 Pré-buscados (dentro da viewport)
2. /archive Não pré-buscado (prefetch={false})
3. /checkout Pré-buscado (prefetch={true}, mesmo não estando na viewport)

Quando um usuário clica no link:
- Páginas pré-buscadas → Exibição em Tempo Real (lidas do cache)
- Páginas Não Pré-buscadas → Exibidas após a requisição de rede

(3) Controles de Rolagem

100%
graph LR
    A[Gatilho de Navegação] --> B{Propriedade scroll}
    B -->|scroll=true Padrão| C[Rolar para o topo da nova página]
    B -->|scroll=false| D[Manter a posição de rolagem atual]

    style C fill:#f8d7da
    style D fill:#d4edda

▶ Exemplo: scroll={false} para manter a posição de rolagem

Saída:

TEXT 📖 Somente leitura
Diagrama: Gatilho de Navegação; Rolar para o topo da nova página; Manter a posição de rolagem atual.
TSX
// ============================================
// Lista de Produtos + Janela Modal: scroll=false Mantém a lista no lugar
// Usuários clicam em um produto para ver seus detalhes, a lista não rola ao retornar
// ============================================

import Link from "next/link";

export default function ProductGrid() {
  const products = Array.from({ length: 20 }, (_, i) => ({
    id: i + 1,
    name: `Produto ${i + 1}`,
  }));

  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-6">Catálogo de Produtos</h1>
      <div className="grid grid-cols-4 gap-4">
        {products.map(p => (
          <Link
            key={p.id}
            href={`/products/${p.id}`}
            scroll={false}
            className="border p-4 rounded hover:shadow-lg"
          >
            <div className="h-32 bg-gray-100 rounded" />
            <p className="mt-2 font-medium">{p.name}</p>
          </Link>
        ))}
      </div>
    </div>
  );
}

Saída:

TEXT 📖 Somente leitura
Mapeia os produtos para renderizar a lista.
Conteúdo: Catálogo de Produtos

Saída:

TEXT 📖 Somente leitura
1. O usuário rolou até o 3º item por página (Rolou 2000px para baixo)
2. Clica no 15º produto para ir à página de detalhes
3. Clica no botão Voltar do navegador
4. Volta para a página de lista, mantém a posição de rolagem em 2000px (Sem "Voltar ao Topo")
5. Comparação: Sem scroll={false}, retorna ao topo toda vez

(4) replace: Substituir histórico

Ação push (padrão) replace
Histórico do Navegador Adiciona uma nova entrada Substitui a entrada atual
Comportamento do Botão Voltar Retorna à página anterior Vai para a página que foi substituída
Após envio de formulário Não pode retornar à página do formulário Pode retornar (pulando a página do formulário)

▶ Exemplo: Usando replace em um cenário de formulário

Saída:

TEXT 📖 Somente leitura
O componente renderiza a UI descrita no navegador.
TSX
// ============================================
// replace Substituição de Histórico: Você não pode retornar à página do formulário após enviar.
// ============================================

import Link from "next/link";

export default function CheckoutPage() {
  return (
    <div className="max-w-md mx-auto p-8">
      <h1 className="text-2xl font-bold mb-6">Checkout</h1>

      <div className="space-y-4">
        <input placeholder="Número do cartão" className="w-full p-3 border rounded" />
        <input placeholder="Data de validade" className="w-full p-3 border rounded" />
        <input placeholder="CVV" className="w-full p-3 border rounded" />
      </div>

      {/* Usando replace: Uma vez enviado, você não pode retornar a esta página. */}
      <Link
        href="/order-confirmation"
        replace
        className="block mt-6 w-full p-3 bg-blue-600 text-white text-center rounded"
      >
        Finalizar Pedido
      </Link>

      <p className="text-sm text-gray-500 mt-2 text-center">
        Após finalizar o pedido, o botão voltar pulará esta página
      </p>
    </div>
  );
}

Saída:

TEXT 📖 Somente leitura
Conteúdo: Checkout | Finalizar Pedido | Após finalizar o pedido, o botão voltar pu

Saída:

TEXT 📖 Somente leitura
Fluxo do Usuário:
1. Início → Carrinho de Compras → Checkout → Página de Confirmação
2. Toque em "Finalizar Pedido" na página de checkout
3. O endereço do navegador muda para /order-confirmation
4. O usuário clica no botão Voltar
5. Pula a página de checkout, vai diretamente para o carrinho de compras
6. Evita que usuários retornem acidentalmente à página de checkout e enviem o pedido novamente


4. Navegação Programática com useRouter

(1) Referência Rápida da API

100%
graph TB
    A[useRouter] --> B[push(url) - Navegar para uma nova página]
    A --> C[replace(url) - Substituir Histórico Atual]
    A --> D[back() - Voltar]
    A --> E[forward() - Avançar]
    A --> F[refresh() - Atualizar esta página]
    A --> G[prefetch(url) - Pré-busca Programática]

    style A fill:#cce5ff
Método Parâmetros Descrição Comportamento do Navegador
push href: string Navegar para a nova URL Adiciona ao histórico
replace href: string Substituir Histórico Atual Substitui o Histórico
back Nenhum Voltar no Navegador Igual a history.back()
forward Nenhum Avançar no Navegador Igual a history.forward()
refresh Nenhum Atualizar esta página Renderização no Servidor (RSC)
prefetch href: string Pré-buscar Página Armazena Payload RSC em Cache

▶ Exemplo: Navegação Programática

Saída:

TEXT 📖 Somente leitura
Diagrama da navegação no lado do cliente usando o componente Link.
TSX
// ============================================
// Exemplo Completo de Navegação Programática com useRouter
// ============================================

'use client';

import { useRouter } from "next/navigation";

export default function NavigationBar({ userId }: { userId: string }) {
  const router = useRouter();

  return (
    <div className="p-4 bg-white shadow-sm">
      <div className="flex gap-4 max-w-4xl mx-auto">
        {/* push: Ir para a Página Inicial */}
        <button
          onClick={() => router.push("/")}
          className="px-4 py-2 bg-blue-600 text-white rounded"
        >
          Início
        </button>

        {/* push: Roteamento Dinâmico */}
        <button
          onClick={() => router.push(`/users/${userId}`)}
          className="px-4 py-2 bg-gray-600 text-white rounded"
        >
          Perfil
        </button>

        {/* replace: Substituir a página atual */}
        <button
          onClick={() => router.replace("/login")}
          className="px-4 py-2 bg-red-600 text-white rounded"
        >
          Sair
        </button>

        {/* back/forward: Histórico de Navegação */}
        <button
          onClick={() => router.back()}
          className="px-4 py-2 border rounded"
        >
          ← Voltar
        </button>
        <button
          onClick={() => router.forward()}
          className="px-4 py-2 border rounded"
        >
          Avançar →
        </button>

        {/* refresh: Atualizar esta página (Re-renderização no Servidor) */}
        <button
          onClick={() => router.refresh()}
          className="px-4 py-2 border rounded"
        >
          Atualizar ↻
        </button>
      </div>
    </div>
  );
}

Saída:

TEXT 📖 Somente leitura
NavigationBar renderiza sua UI.

Saída:

TEXT 📖 Somente leitura
Clique em cada botão:
[Início]    → Navega para /
[Perfil]    → Navega para /users/123
[Sair]      → Substitui o histórico atual por /login (Sem Retorno)
[← Voltar]  → Botão Voltar do Navegador
[Avançar]   → Botão Avançar do Navegador
[Atualizar] → Atualiza esta página (RSC Renderiza Novamente, Não causa recarregamento completo da página)

▶ Exemplo: Navegação Após Envio de Formulário

Saída:

TEXT 📖 Somente leitura
A página renderiza conforme descrito acima, com a UI atualizando com base no comportamento descrito.
TSX
// ============================================
// Envio de Formulário: Verificação → salvar → Navegação Programática
// ============================================

'use client';

import { useRouter } from "next/navigation";
import { useState } from "react";

export default function CreateProjectForm() {
  const router = useRouter();
  const [saving, setSaving] = useState(false);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setSaving(true);

    try {
      // Simulação de Chamada API
      await new Promise(resolve => setTimeout(resolve, 1500));
      // Após conclusão bem-sucedida, navega para a página do novo projeto
      router.push("/projects/42");
      // e atualiza o cache de dados
      router.refresh();
    } catch (err) {
      console.error("Falha ao criar projeto:", err);
      alert("Falha ao criar projeto. Por favor, tente novamente.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="max-w-lg mx-auto p-8 space-y-4">
      <h1 className="text-2xl font-bold">Criar Projeto</h1>
      <input
        name="name"
        placeholder="Nome do projeto"
        className="w-full p-3 border rounded"
        required
      />
      <textarea
        name="description"
        placeholder="Descrição"
        className="w-full p-3 border rounded h-32"
      />
      <button
        type="submit"
        disabled={saving}
        className="w-full p-3 bg-blue-600 text-white rounded disabled:opacity-50"
      >
        {saving ? "Criando..." : "Criar Projeto"}
      </button>
    </form>
  );
}

Saída:

TEXT 📖 Somente leitura
Formulário com: name.
Conteúdo: Criar Projeto

Saída:

TEXT 📖 Somente leitura
1. O usuário insere o nome e a descrição do projeto
2. Clica no botão "Criar Projeto"
3. O botão muda para "Criando..." (Desabilitado)
4. Após 1,5s (atraso simulado da API)
5. Navegação da Página para /projects/42
6. Atualiza os Dados da Lista de Projetos (Inclui projetos recém-criados)


5. redirect() e <Redirect>

(1) Dois Tipos de Redirecionamento

100%
graph TB
    A[Requisições de Redirecionamento] --> B[Lado do Servidor<br/>redirect()]
    A --> C[Lado do Cliente<br/><Redirect>]
    B --> D[Server Action / Route Handler]
    B --> E[Server Component]
    C --> F[Client Component]
    C --> G[Ao usar renderização condicional]

    style B fill:#d4edda
    style C fill:#cce5ff
Método Onde Usar Quando Disparar Performance
redirect() Server Actions / Server Components Durante a Resposta do Servidor Redirecionamento no Servidor, Zero Sobrecarga no Cliente
<Redirect> Client Components Durante a renderização Redirecionamento no lado do cliente, mudanças de rota


6. Soft Navigation vs Hard Navigation

(1) Comparação

100%
graph TB
    subgraph "Soft Navigation (Link/useRouter)"
        A[Roteamento no Lado do Cliente] --> B[Navegação de Página<br/>Sem recarregamento completo]
        B --> C[layout Mantém Montado]
        B --> D[Estado React Preservado]
        B --> E[Substituição do Payload RSC]
    end

    subgraph "Hard Navigation (Atualizar toda a página do navegador)"
        F[Carregamento Completo da Página no Navegador] --> G[Navegação de Página<br/>Atualizar a página inteira]
        G --> H[layout Remonta]
        G --> I[Reinicia Todos os Estados]
        G --> J[Carregamento Completo de JS/CSS]
    end

    style A fill:#d4edda
    style F fill:#f8d7da
Característica Soft Navigation Hard Navigation
Método de Disparo <Link>, useRouter() Atualização do navegador, tag <a>, window.location
Atualiza Página Inteira ❌ Não ✅ Sim
Layout Preservado ✅ Preservado ❌ Remontado
Payload RSC Atualização Incremental Download Completo
Performance Tempo real 500 ms–2 s
Estado Mantido


7. Indicador de Carregamento de Navegação

(1) Plano de Implementação

100%
graph TB
    A[Gatilho de Navegação] --> B[Indicador de Carregamento]
    B --> C[Barra de Progresso Superior<br/>Estilo NProgress]
    B --> D[Suspense fallback<br/>Exibição de Estrutura Wireframe]
    B --> E[loading.tsx<br/>Carregamento em Nível de Página]

    style A fill:#cce5ff
    style B fill:#f8d7da


8. Exemplo Completo: Sistema de Navegação de E-commerce

TSX
// ============================================
// Exemplo Abrangente: Um Sistema de Navegação Completo para E-commerce
// Cobrindo Link, useRouter, redirect, Estado de Carregamento de Navegação
// ============================================

// src/app/layout.tsx — Barra de Navegação Global
import Link from "next/link";
import { CartCount } from "./CartCount";

export default function RootLayout({ children }) {
  return (
    <html lang="pt-BR">
      <body>
        <header className="bg-white shadow-sm sticky top-0 z-40">
          <div className="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
            <Link href="/" className="text-2xl font-bold text-blue-600">
              ShopHub
            </Link>

            <nav className="hidden md:flex gap-6">
              <Link href="/products" className="hover:text-blue-600">
                Produtos
              </Link>
              <Link href="/categories" className="hover:text-blue-600">
                Categorias
              </Link>
              <Link href="/deals" prefetch={false} className="hover:text-blue-600">
                Ofertas
              </Link>
              <Link href="/about" className="hover:text-blue-600">
                Sobre
              </Link>
            </nav>

            <div className="flex items-center gap-4">
              <Link href="/search" className="text-gray-600 hover:text-blue-600">
                Buscar
              </Link>
              <Link href="/cart" className="relative text-gray-600 hover:text-blue-600">
                Carrinho
                <CartCount />
              </Link>
              <Link
                href="/account"
                className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm"
              >
                Entrar
              </Link>
            </div>
          </div>
        </header>

        <main className="max-w-6xl mx-auto px-4 py-8">
          {children}
        </main>
      </body>
    </html>
  );
}

// src/app/products/[id]/page.tsx — Detalhes do Produto (Inclui lógica de navegação)
'use client';
import { useRouter } from "next/navigation";
import Link from "next/link";

export default function ProductDetail({ params }) {
  const router = useRouter();

  async function handleAddToCart() {
    await fetch("/api/cart", {
      method: "POST",
      body: JSON.stringify({ productId: params.id }),
    });
    // Atualiza a quantidade no carrinho de compras após adicionar um item
    router.refresh();
  }

  async function handleBuyNow() {
    await fetch("/api/cart", {
      method: "POST",
      body: JSON.stringify({ productId: params.id, quantity: 1 }),
    });
    // Comprar Agora → Ir para o Checkout (usa replace para evitar retornar à página do produto)
    router.replace("/checkout");
  }

  return (
    <div>
      {/* Navegação Breadcrumb */}
      <nav className="text-sm text-gray-500 mb-6">
        <Link href="/" className="hover:text-blue-600">Início</Link>
        <span className="mx-2">/</span>
        <Link href="/products" className="hover:text-blue-600">Produtos</Link>
        <span className="mx-2">/</span>
        <span className="text-gray-900">{params.id}</span>
      </nav>

      <div className="flex gap-8">
        <div className="w-1/2">
          <img
            src={`https://picsum.photos/seed/${params.id}/400/400`}
            alt="Produto"
            className="w-full rounded-lg"
          />
        </div>
        <div className="w-1/2">
          <h1 className="text-3xl font-bold">Produto #{params.id}</h1>
          <p className="text-2xl text-green-600 font-bold mt-4">R$ 49,99</p>
          <p className="text-gray-600 mt-4">
            Produto de alta qualidade com recursos premium.
          </p>

          <div className="flex gap-4 mt-8">
            <button
              onClick={handleAddToCart}
              className="flex-1 px-6 py-3 border-2 border-blue-600 text-blue-600 rounded-lg hover:bg-blue-50"
            >
              Adicionar ao Carrinho
            </button>
            <button
              onClick={handleBuyNow}
              className="flex-1 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
            >
              Comprar Agora
            </button>
          </div>

          <div className="mt-8 border-t pt-6">
            <Link
              href={`/products/${Number(params.id) - 1}`}
              scroll={false}
              className="text-blue-600 hover:underline"
            >
              ← Produto Anterior
            </Link>
            <Link
              href={`/products/${Number(params.id) + 1}`}
              scroll={false}
              className="text-blue-600 hover:underline float-right"
            >
              Próximo Produto →
            </Link>
          </div>
        </div>
      </div>
    </div>
  );
}

// src/app/CartCount.tsx — Exibição da Quantidade no Carrinho de Compras
export async function CartCount() {
  const cart = await fetch("https://api.example.com/cart");
  const itemCount = cart?.items?.length ?? 0;

  if (itemCount === 0) return null;

  return (
    <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs w-5 h-5 rounded-full flex items-center justify-center">
      {itemCount}
    </span>
  );
}

Saída Esperada:

TEXT 📖 Somente leitura
Barra de Navegação (Fixada no topo):
[ShopHub]  Produtos  Categorias  Ofertas  Sobre  [Buscar]  Carrinho(3)  [Entrar]

Página de Detalhes do Produto (/products/1):
Início / Produtos / 1
[Imagens do Produto]  [Detalhes do Produto]
            Produto #1
            R$ 49,99
            [Adicionar ao Carrinho] [Comprar Agora]
            ← Produto Anterior | Próximo Produto →

Comportamento de Navegação:
- Clicar em Produtos → Soft Nav, Troca Instantânea de Página
- Clicar em Ofertas → Sem pré-busca (prefetch=false), Mas a navegação ainda é rápida
- Adicionar ao Carrinho → router.refresh(), Atualização do Ícone do Carrinho
- Comprar Agora → router.replace("/checkout"), Substituição de Histórico

❓ Perguntas Frequentes

P: Qual é a diferença entre o componente Link e a tag <a>? R: O componente Link gerencia o roteamento no lado do cliente sem disparar um recarregamento completo da página, preserva o layout e suporta pré-carregamento. A tag <a> dispara um recarregamento completo da página (hard navigation), fazendo com que todo o estado React seja perdido. O Next.js usa Link para todo o roteamento interno.

P: Por que componentes que usam useRouter precisam incluir 'use client'? R: useRouter é um hook do React e só pode ser usado em componentes cliente. Componentes servidor não têm acesso às APIs do navegador (como history e location), então os hooks relacionados ao roteamento são executados no lado do cliente.

P: Qual é a diferença entre router.refresh() e window.location.reload()? R: router.refresh() é a atualização suave do Next.js — ele solicita novamente o payload RSC do servidor, atualizando apenas as partes alteradas enquanto preserva o estado do lado do cliente (como useState e Context). window.location.reload() realiza um recarregamento completo da página, causando a perda de todo o estado e exigindo o recarregamento completo de JS e CSS.

P: O prefetch do Link também é acionado em dispositivos móveis? R: Sim. O prefetch é acionado para links dentro da viewport, inclusive em dispositivos móveis. No entanto, o Next.js leva em consideração o uso de dados do usuário — pode não pré-buscar páginas grandes em redes lentas (2G/3G). Você também pode controlar isso manualmente usando prefetch={false}.

P: Quando redirect() e <Redirect> devem ser usados? R: redirect() é usado no lado do servidor (Server Actions, Server Components, Route Handlers) para enviar uma resposta 303/307. Para redirecionamento condicional no lado do cliente, use lógica de controle de acesso (exiba uma mensagem de "Acesso Negado" + um botão de navegação) em vez do componente <Redirect>.

P: O que devo fazer se a URL mudar, mas o conteúdo da página permanecer o mesmo durante a navegação? R: Isso geralmente é causado pelo uso da mesma React key ou cache. Você pode tentar o seguinte: 1) Use uma key diferente no componente da página para forçar a reconstrução; 2) Chame router.refresh() para atualizar os dados do lado do servidor; 3) Verifique se os dados estão sendo armazenados em cache pelo layout.


📖 Resumo


📝 Exercícios

  1. Exercício Básico (⭐): Crie cinco links na página (Início, Sobre, Contato, Lista de Produtos, Detalhes do Produto #42) e observe quais URLs são pré-carregadas na aba Network do navegador.

  2. Exercício Avançado (⭐⭐): Implemente um fluxo de trabalho "Login → Dashboard": Use uma Server Action para verificar o login; se bem-sucedido, use redirect() para redirecionar para o dashboard; se malsucedido, use redirect() para retornar à página de login e inclua os parâmetros de erro.

  3. Desafio (⭐⭐⭐): Implemente uma página de lista com rolagem infinita. Quando um usuário clicar em um produto para ir à página de detalhes, use scroll={false} para preservar a posição de rolagem; quando o usuário retornar, a lista deve permanecer em sua posição anterior. Use router.back() para implementar a navegação de volta.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%