//
Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Trate erros graciosamente no App Router do Next.js 15+ usando limites de erro error.tsx, global-error.tsx para falhas no nível raiz, not-found.tsx para 404s e logging estruturado.
// app/dashboard/error.tsx
"use client";
import { useEffect } from "react";
export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Registre em seu serviço de relatórios de erros
console.error("Erro no Dashboard:", error);
}, [error]);
return (
<div role="alert">
<h2>Ocorreu um erro</h2>
<p>{error.message}</p>
{error.digest && (
<p className="text-sm text-gray-500">ID do Erro: {error.digest}</p>
)}
<button onClick={reset}>Tentar novamente</button>
</div>
);
}// app/global-error.tsx
"use client";
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<div role="alert">
<h1>Erro na Aplicação</h1>
<p>Ocorreu um erro inesperado.</p>
<button onClick={reset}>Recarregar</button>
</div>
</body>
</html>
);
}// app/not-found.tsx
import Link from "next/link";
export default function NotFound() {
return (
<div>
<h1>404 - Página Não Encontrada</h1>
<p>A página que você procura não existe.</p>
<Link href="/">Ir para a página inicial</Link>
</div>
);
}// app/posts/[slug]/page.tsx
import { notFound } from "next/navigation";
export default async function PostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await db.post.findUnique({ where: { slug } });
if (!post) {
notFound(); // Renderiza o not-found.tsx mais próximo
}
return <article>{post.content}</article>;
}// lib/logger.ts
type ErrorContext = {
userId?: string;
path?: string;
action?: string;
metadata?: Record<string, unknown>;
};
export function logError(error: unknown, context?: ErrorContext) {
const errorObj = error instanceof Error ? error : new Error(String(error));
const payload = {
message: errorObj.message,
stack: errorObj.stack,
timestamp: new Date().toISOString(),
...context,
};
// Substitua por Sentry, Axiom ou seu serviço preferido
if (process.env.NODE_ENV === "production") {
fetch("/api/log", {
method: "POST",
body: JSON.stringify(payload),
}).catch(() => {
// Engole erros de logging para prevenir falhas em cascata
});
} else {
console.error("[Erro]", payload);
}
}// app/actions.ts
"use server";
import { logError } from "@/lib/logger";
type ActionResult<T> =
| { success: true; data: T }
| { success: false; error: string };
export async function createPost(
formData: FormData
): Promise<ActionResult<{ id: string }>> {
try {
const title = formData.get("title") as string;
if (!title) {
return { success: false, error: "O título é obrigatório" };
}
const post = await db.post.create({ data: { title } });
return { success: true, data: { id: post.id } };
} catch (error) {
logError(error, { action: "createPost" });
return { success: false, error: "Falha ao criar post" };
}
}error.tsx é um Componente Cliente que envolve os filhos do segmento da rota em um Limite de Erro do React. Ele captura erros lançados durante a renderização, em Componentes Servidor e durante a busca de dados.global-error.tsx captura erros no layout raiz. Ele deve renderizar suas próprias tags <html> e <body> porque substitui todo o layout raiz quando ativado.not-found.tsx é renderizado quando notFound() é chamado ou quando nenhuma rota corresponde. O not-found.tsx mais próximo na árvore de componentes é usado.useEffect ou código assíncrono em Componentes Cliente. Use try/catch para esses.digest é um hash gerado pelo Next.js para erros do lado do servidor. Ele permite correlacionar erros voltados para o usuário com logs do servidor sem expor traces de pilha confidenciais.reset tenta re-renderizar os filhos do limite de erro. Funciona para erros transitórios (problemas de rede), mas não para bugs persistentes.Limite de Erro com Tentativa e Fallback:
"use client";
import { useEffect, useState } from "react";
export default function ErrorWithRetry({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
const [retryCount, setRetryCount] = useState(0);
useEffect(() => {
if (retryCount > 0) {
reset();
}
}, [retryCount, reset]);
if (retryCount >= 3) {
return (
<div>
<h2>Erro Persistente</h2>
<p>Por favor, contate o suporte. ID do Erro: {error.digest}</p>
</div>
);
}
return (
<div role="alert">
<h2>Ocorreu um Erro</h2>
<button onClick={() => setRetryCount((c) => c + 1)}>
Tentar novamente ({3 - retryCount} tentativas restantes)
</button>
</div>
);
}Tratamento de Erros de Route Handler:
// app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
import { logError } from "@/lib/logger";
export async function GET(request: NextRequest) {
try {
const posts = await db.post.findMany();
return NextResponse.json(posts);
} catch (error) {
logError(error, { path: "/api/posts", action: "GET" });
return NextResponse.json(
{ error: "Erro interno do servidor" },
{ status: 500 }
);
}
}error é Error & { digest?: string }. O digest é opcional e só está presente para erros do lado do servidor.{ success: true; data: T } | { success: false; error: string }) para tratamento de erros type-safe no cliente.throw em Server Actions para erros de validação. Reserve throw para falhas inesperadas que devem acionar o limite de erro.error.tsx deve ser um Componente Cliente. Requer a diretiva "use client". Esquecer isso gera um erro de build.error.tsx não captura erros no layout.tsx do mesmo nível. Para capturar erros de layout, coloque error.tsx no segmento pai, ou use global-error.tsx para o layout raiz.global-error.tsx só ativa em produção. Em desenvolvimento, a sobreposição de erros do Next.js é mostrada em vez disso.redirect() lança um erro especial. Se você envolver redirect() em um try/catch dentro de um Componente Servidor, o redirecionamento será capturado e engolido. Ou relance os erros NEXT_REDIRECT ou chame redirect() fora do try/catch.digest para correlacionar com logs do servidor.| Abordagem | Prós | Contras |
|---|---|---|
Limite error.tsx | Embutido, automático, por rota | Apenas Componente Cliente, sem erros de layout |
global-error.tsx | Captura erros do layout raiz | Deve renderizar seu próprio html/body, apenas produção |
| Try/catch em Server Actions | Controle granular, retorna erros tipados | Manual, sem limite automático |
| Sentry ou Datadog | Rastreamento rico de erros, alertas | Dependência externa, custo |
Classe ErrorBoundary do React | Controle total, reutilizável | Verboso, sem erros de Componente Servidor |
"use client" é necessária para que error.tsx funcione como um limite de erro.error.tsx só captura erros nos filhos do segmento da rota.error.tsx no segmento pai.global-error.tsx.global-error.tsx é ativado, ele substitui todo o layout raiz.<html> e <body>, a página não teria estrutura de documento.redirect() lança um erro especial NEXT_REDIRECT.NEXT_REDIRECT ou chame redirect() fora do bloco try/catch.throw para falhas inesperadas que devem acionar o limite de erro mais próximo.{ success: false; error: string }) para erros de validação esperados.reset() re-renderiza os filhos do limite de erro, tentando a recuperação.type ActionResult<T> =
| { success: true; data: T }
| { success: false; error: string };
export async function createPost(
formData: FormData
): Promise<ActionResult<{ id: string }>> {
// ...
}success permite que o TypeScript restrinja o tipo ao verificar o resultado.export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
// ...
}Error & { digest?: string } é o tipo necessário. O digest é opcional..catch(() => {}) garante que as falhas de logging sejam silenciosas.digest é exposto, que você pode usar para encontrar o erro completo nos logs do servidor.useEffect.layout.tsx do mesmo nível.global-error.tsx para falhas de layout raiz.Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥