40 Regras do Next.js
Regras focadas especificamente na construção de aplicações Next.js com o App Router. Arquitetura, roteamento, renderização, implantação e melhores práticas operacionais.
Busque em todas as páginas da documentação
Regras focadas especificamente na construção de aplicações Next.js com o App Router. Arquitetura, roteamento, renderização, implantação e melhores práticas operacionais.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
1. Uma rota, uma responsabilidade. Cada page.tsx deve representar uma única visualização. Se um arquivo de página crescer além de 100 linhas, extraia componentes para arquivos colocados.
2. Use grupos de rotas para organizar sem afetar URLs. (marketing), (dashboard), (auth) mantêm sua árvore de arquivos limpa sem adicionar segmentos de URL.
app/
(marketing)/
page.tsx # /
pricing/page.tsx # /pricing
(dashboard)/
dashboard/page.tsx # /dashboard
settings/page.tsx # /settings
3. Sempre forneça loading.tsx para rotas dinâmicas. Cada segmento de rota que busca dados deve ter um loading.tsx. Os usuários nunca devem ver uma tela em branco enquanto os dados carregam.
4. Sempre forneça error.tsx para cada segmento de rota. Erros acontecem. Capture-os graciosamente com error.tsx. Inclua um botão de tentar novamente e uma maneira de navegar para longe.
"use client";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Algo deu errado</h2>
<button onClick={reset}>Tentar novamente</button>
</div>
);
}5. Use not-found.tsx para páginas 404 personalizadas. Chame notFound() de Server Components quando os dados não existirem. Forneça uma página de não encontrado útil por segmento de rota.
6. Use layouts para UI compartilhada, templates para reset de estado por navegação. Layouts persistem entre navegações (bom para sidebars, nav). Templates remontam em cada navegação (bom para animações, analytics por página).
7. Use pastas privadas (_components, _lib) para arquivos não relacionados a rotas. Tudo que começa com _ é excluído do sistema de roteamento. Coloque helpers sem criar rotas acidentalmente.
8. Use rotas paralelas (@slots) para layouts complexos. Dashboards com painéis que carregam independentemente, padrões de modal e conteúdo condicional se beneficiam de rotas paralelas @slot com fallbacks default.tsx.
9. Use rotas interceptadoras para padrões de modal. (.)photo/[id] intercepta a navegação para mostrar um modal enquanto preserva a URL compartilhável. A página completa é renderizada na navegação direta ou atualização.
10. Mantenha o middleware enxuto. O middleware é executado em cada solicitação. Use-o apenas para redirecionamentos de autenticação, detecção de locale, cabeçalhos de teste A/B e reescritas de caminho. Nunca busque dados ou faça computação pesada no middleware.
// middleware.ts - mantenha-o rápido
export function middleware(request: NextRequest) {
const session = request.cookies.get("session");
if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
}
export const config = {
matcher: ["/dashboard/:path*", "/settings/:path*"],
};11. Opte por renderização estática por padrão. Páginas sem dados dinâmicos devem ser geradas estaticamente. Esta é a opção mais rápida. O Next.js faz isso automaticamente quando não há funções dinâmicas.
12. Entenda o que torna uma rota dinâmica. Usar cookies(), headers(), searchParams ou fetch com cache: "no-store" opta toda a rota para renderização dinâmica. Seja intencional sobre isso.
| Função | Efeito |
|---|---|
cookies() | Torna a rota dinâmica |
headers() | Torna a rota dinâmica |
searchParams | Torna a rota dinâmica |
fetch com cache: "no-store" | Torna a rota dinâmica |
unstable_noStore() | Torna a rota dinâmica |
13. Use generateStaticParams para rotas dinâmicas conhecidas. Pré-renderize páginas de produtos, posts de blog e outros conteúdos com slugs conhecidos no momento da compilação.
export async function generateStaticParams() {
const posts = await db.post.findMany({ select: { slug: true } });
return posts.map((post) => ({ slug: post.slug }));
}14. Use ISR (revalidação) para conteúdo que muda periodicamente. Defina revalidate para atualizar páginas em cache sem uma reconstrução completa.
// Revalida a cada hora
export const revalidate = 3600;
// Ou por fetch
const data = await fetch(url, { next: { revalidate: 3600 } });15. Use limites de Suspense estrategicamente. Não envolva a página inteira em um único limite de Suspense. Envolva cada seção de busca de dados separadamente para que elas façam streaming independentemente.
16. Defina dimensões de imagem ou use o modo fill. Cada next/image deve ter width/height explícito ou usar fill com um contêiner dimensionado. Isso evita Cumulative Layout Shift (CLS).
17. Use priority em imagens acima da dobra. A imagem principal e o elemento LCP devem ter priority={true} para pré-carregar imediatamente.
18. Configure remotePatterns em vez de domains. remotePatterns em next.config.ts oferece controle granular sobre fontes de imagem externas permitidas com correspondência de protocolo e pathname.
19. Use next/font para todas as fontes. Fontes auto-hospedadas via next/font eliminam o layout shift do carregamento de fontes e evitam requisições de rede externas para o CDN do Google Fonts.
20. Habilite o Turbopack para desenvolvimento. Adicione --turbopack ao seu script de desenvolvimento. Ele é estável no Next.js 15+ e significativamente mais rápido para HMR.
{
"scripts": {
"dev": "next dev --turbopack"
}
}21. Busque dados em Server Components, não em Client Components. Server Components têm acesso direto a bancos de dados, sistemas de arquivos e APIs internas. Nenhuma camada de API é necessária.
22. Use Server Actions para todas as mutações. Formulários, cliques de botão e qualquer operação de escrita devem passar por Server Actions. Eles lidam com proteção CSRF, aprimoramento progressivo e atualizações otimistas.
23. Valide entradas de Server Action com Zod. Nunca confie em dados de formulário. Analise e valide no servidor antes de processar.
"use server";
import { z } from "zod";
const schema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(10),
});
export async function createPost(formData: FormData) {
const parsed = schema.safeParse(Object.fromEntries(formData));
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors };
}
await db.post.create({ data: parsed.data });
revalidatePath("/posts");
}24. Sempre chame revalidatePath ou revalidateTag após mutações. UI desatualizada após uma ação bem-sucedida é um bug comum. Revalide os caminhos afetados.
25. Use Route Handlers (GET, POST) para endpoints de webhook e consumidores de API externos. Server Actions são para submissões de formulário internas. Route Handlers são para integrações externas, webhooks e APIs REST.
26. Proteja cada Server Action com autenticação. Server Actions são endpoints HTTP públicos. Verifique a sessão no topo de cada ação.
"use server";
export async function deletePost(id: string) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
if (session.user.role !== "admin") throw new Error("Forbidden");
await db.post.delete({ where: { id } });
revalidatePath("/posts");
}27. Use revalidateTag para invalidação de cache granular. Marque seus fetches, depois invalide apenas o que mudou. Mais eficiente que revalidatePath para relacionamentos de dados complexos.
// Buscando com tags
const posts = await fetch(url, { next: { tags: ["posts"] } });
// Invalidando
revalidateTag("posts");28. Use redirect em Server Actions para navegação pós-mutação. Chame redirect("/success") após uma ação bem-sucedida. Ele lança um erro internamente, então coloque-o fora do try/catch.
29. Lide com erros de Server Action com valores de retorno, não com erros lançados. Retorne { errors: "message" } das ações e lide na UI com useActionState. Lance erros apenas para casos verdadeiramente excepcionais.
30. Use o pacote server-only para evitar que código de servidor vaze para o cliente. Importe "server-only" no topo de qualquer módulo que nunca deva ser empacotado no cliente (clientes de banco de dados, chaves secretas, etc.).
import "server-only";
import { db } from "./db";
export async function getSecretData() {
return db.secrets.findMany();
}31. Use variáveis de ambiente corretamente. Prefixo NEXT_PUBLIC_ apenas para valores seguros para o cliente. Todo o resto é exclusivo do servidor. Valide variáveis de ambiente na inicialização com Zod.
32. Configure next.config.ts minimamente. Adicione apenas a configuração que você precisa. Evite recursos experimentais em produção, a menos que sejam completamente testados.
33. Use saída standalone para implantações Docker. output: "standalone" cria um build autocontido com apenas as dependências necessárias.
// next.config.ts
const config = {
output: "standalone",
};
export default config;34. Defina cabeçalhos de segurança adequados. Configure Content-Security-Policy, X-Frame-Options, X-Content-Type-Options e Referrer-Policy em next.config.ts ou middleware.
35. Gere sitemap.xml e robots.txt dinamicamente. Use app/sitemap.ts e app/robots.ts para geração dinâmica baseada no seu conteúdo.
36. Use generateMetadata para páginas dinâmicas. Metadados estáticos para páginas fixas, função generateMetadata para páginas dinâmicas (posts de blog, produtos).
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return {
title: post.title,
description: post.excerpt,
openGraph: { images: [post.coverImage] },
};
}37. Teste com next build e next start antes de implantar. O modo de desenvolvimento esconde muitos problemas. Sempre teste em modo de produção localmente.
38. Monitore o tamanho do bundle. Use @next/bundle-analyzer periodicamente para detectar crescimento inesperado do bundle. Defina orçamentos para JavaScript do cliente.
39. Fixe as versões do Next.js em produção. Use versões exatas ("next": "16.2.2") e não intervalos. Atualize intencionalmente após ler os changelogs.
40. Use instrumentation.ts para tarefas de inicialização do servidor. Aquecimento da conexão do banco de dados, inicialização de telemetria e configuração única pertencem ao arquivo de instrumentação.
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
// Inicializa pool de conexão com o banco de dados
// Configura rastreamento de erros (Sentry, etc.)
}
}Qualquer um destes opta a rota para renderização dinâmica:
cookies() ou headers()searchParamsfetch com cache: "no-store"unstable_noStore()export async function generateStaticParams() {
const posts = await db.post.findMany({ select: { slug: true } });
return posts.map((post) => ({ slug: post.slug }));
}{ error: "message" } da ação em vez de lançar um errouseActionStateredirect() dentro de try/catch quebrará porque redirect lança um erro internamenteimport "server-only";type Props = {
params: Promise<{ slug: string }>;
};
export default async function Page({ params }: Props) {
const { slug } = await params;
// ...
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
// ...
}"use client";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return <button onClick={reset}>Tentar novamente</button>;
}Nota: error.tsx deve ser um Client Component.
process.env.NEXT_RUNTIME para distinguir entre os runtimes Node.js e EdgeremotePatterns permite controle granular com correspondência de protocolo, hostname, porta e pathnamedomains apenas corresponde ao hostname e é menos seguroremotePatterns impede o carregamento de imagens de caminhos inesperados em um domínio permitidoUse Zod para validar variáveis de ambiente para que o aplicativo falhe rapidamente com um erro claro:
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXT_PUBLIC_APP_URL: z.string().url(),
});
export const env = envSchema.parse(process.env);| Categoria | Regra Chave |
|---|---|
| Roteamento | Uma rota, uma responsabilidade. Sempre forneça loading + error. |
| Renderização | Padrão estático. Seja intencional sobre o dinâmico. |
| Dados | Fetch do servidor por padrão. Server Actions para mutações. |
| Segurança | Autenticação em cada ação. Valide todas as entradas. Nunca exponha segredos. |
| Desempenho | Suspense por seção. Imagens prioritárias. Turbopack em dev. |
| Implantação | Saída standalone. Teste com next build + next start. |
Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥