1

App Router do Next.js 15: Layouts, Loading States e Error Boundaries na Prática

O problema que o App Router resolve (e o que ele cria)

O Pages Router do Next.js tratava cada rota como uma ilha. Compartilhar estado de layout entre páginas exigia _app.tsx, _document.tsx e uma coreografia frágil de getLayout patterns. O App Router inverte essa lógica: a árvore de arquivos define a árvore de componentes. Layouts persistem entre navegações, loading states operam por segmento, e error boundaries capturam falhas sem derrubar a aplicação inteira.

Mas essa inversão traz armadilhas. Layouts que re-renderizam quando não deveriam. Loading states que cobrem a tela inteira em vez de um trecho. Error boundaries que engolem erros silenciosamente. Este post mostra como montar cada peça com precisão e onde a maioria dos projetos erra.

Layouts: persistência que funciona a seu favor

Um layout.tsx no App Router é um React Server Component por padrão. Ele recebe children e não re-renderiza quando o usuário navega entre rotas filhas. Isso significa que um sidebar, um header ou um provider de tema sobrevive à navegação sem perder estado.

// app/dashboard/layout.tsx
import { ReactNode } from "react";
import { Sidebar } from "@/components/sidebar";
import { getSession } from "@/lib/auth";
import { redirect } from "next/navigation";

// Server Component: roda apenas no servidor, sem bundle pro client
export default async function DashboardLayout({
  children,
}: {
  children: ReactNode;
}) {
  const session = await getSession();

  // Redireciona antes de renderizar qualquer coisa,
  // evitando flash de conteúdo protegido
  if (!session) {
    redirect("/login");
  }

  return (
    <div className="flex min-h-screen">
      <Sidebar user={session.user} />
      <main className="flex-1 p-6">{children}</main>
    </div>
  );
}

O getSession() roda no servidor a cada request, mas o layout em si não re-monta no client durante navegação client-side. O React preserva a árvore do layout e troca apenas o children. Isso é diferente de um _app.tsx no Pages Router, que re-executava a cada transição.

Layouts aninhados com escopo preciso

A composição real aparece quando você aninha layouts. Cada segmento de rota pode ter seu próprio layout, e eles se empilham:

// app/dashboard/projects/layout.tsx
import { ReactNode } from "react";
import { ProjectNav } from "@/components/project-nav";

export default function ProjectsLayout({
  children,
}: {
  children: ReactNode;
}) {
  return (
    <div>
      <ProjectNav />
      {/* Apenas essa região troca quando o usuário navega
          entre /dashboard/projects/[id] */}
      <section className="mt-4">{children}</section>
    </div>
  );
}

A estrutura de pastas app/dashboard/layout.tsx + app/dashboard/projects/layout.tsx produz dois layouts encaixados. Quando o usuário navega de /dashboard/projects/1 para /dashboard/projects/2, o DashboardLayout e o ProjectsLayout persistem. Apenas o page.tsx dentro de projects/[id] re-renderiza.

Se você precisa de proteção de rotas nesse fluxo, o layout do dashboard já cuida disso. O layout de projects não precisa repetir a verificação. Para estratégias mais avançadas de autenticação com middleware, o post sobre autenticação com NextAuth.js e Middleware cobre o fluxo completo.

Loading states: granularidade por segmento

O arquivo loading.tsx cria um Suspense boundary automático ao redor do page.tsx do mesmo segmento. Quando o componente da página é um Server Component assíncrono, o Next.js mostra o loading.tsx enquanto aguarda a resolução.

// app/dashboard/projects/loading.tsx
export default function ProjectsLoading() {
  return (
    <div className="grid grid-cols-3 gap-4">
      {Array.from({ length: 6 }).map((_, i) => (
        <div
          key={i}
          className="h-32 animate-pulse rounded-lg bg-zinc-800"
        />
      ))}
    </div>
  );
}
// app/dashboard/projects/page.tsx
import { Proje

---

Leia o artigo completo em [https://www.vivodecodigo.com.br/nextjs/nextjs-app-router-layouts-loading-error-boundaries-pratica](https://www.vivodecodigo.com.br/nextjs/nextjs-app-router-layouts-loading-error-boundaries-pratica)
Carregando publicação patrocinada...