TypeScript com Next.js App Router 2026
Setup completo de TypeScript no Next.js App Router. De page e layout types até server components, loading, error e next.config.ts, tudo tipado e funcionando.
Por que isso é importante
TypeScript com Next.js App Router 2026. Setup completo de TypeScript no Next.js App Router. De page e layout types até server components, loading, error e next.config.ts, tudo tipado e funcionando.
Setup Inicial: TypeScript no Next.js
O Next.js já vem com suporte nativo a TypeScript. Quando você cria um projeto com create-next-app, ele pergunta se quer TypeScript. Se disse sim, o tsconfig.json já tá configurado. Se disse não, dá pra adicionar depois.
O App Router trouxe mudanças pesadas na tipagem. Server Components são o padrão. Client Components precisam da diretiva 'use client'. Cada arquivo especial (page, layout, loading, error, not-found) tem sua assinatura de tipos. Dominar essas assinaturas é o que separa código limpo de código cheio de any.
Vamos configurar tudo do zero. Se seu projeto já existe, pule pro passo que faz sentido.
Passo a Passo: Configuração Completa
Do projeto novo até o TypeScript rodando com strict mode. Sem atalhos.
Exemplos Práticos: Tipando Cada Arquivo
Cada arquivo especial do App Router tem sua assinatura. Vamos ver todas.
tsconfig.json Otimizado pra Next.js
// tsconfig.json - configuração recomendada
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{ "name": "next" }
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
next.config.ts com Tipagem
// next.config.ts (TypeScript nativo no Next.js 15+)
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.unsplash.com",
},
],
},
experimental: {
typedRoutes: true, // ativa tipagem de rotas!
},
};
export default nextConfig;
layout.tsx: Root Layout Tipado
// app/layout.tsx
import type { Metadata } from "next";
import type { ReactNode } from "react";
// Tipo das props do layout
type RootLayoutProps = {
children: ReactNode;
};
export const metadata: Metadata = {
title: {
template: "%s | MeuApp",
default: "MeuApp",
},
description: "Descrição do app",
};
export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang="pt-BR">
<body>{children}</body>
</html>
);
}
// Layout com múltiplos slots (parallel routes)
type DashboardLayoutProps = {
children: ReactNode;
analytics: ReactNode;
notifications: ReactNode;
};
export default function DashboardLayout({
children,
analytics,
notifications,
}: DashboardLayoutProps) {
return (
<div>
{children}
{analytics}
{notifications}
</div>
);
}
page.tsx: Server Component Padrão
// app/page.tsx - Home (sem params)
export default function HomePage() {
// Server Component por padrão
// Pode usar async/await direto
return <h1>Home</h1>;
}
// app/blog/[slug]/page.tsx - Com params
type PageProps = {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};
export default async function BlogPage({ params, searchParams }: PageProps) {
const { slug } = await params;
const { page } = await searchParams;
const post = await getPost(slug);
return (
<article>
<h1>{post.title}</h1>
</article>
);
}
loading.tsx e error.tsx Tipados
// app/blog/loading.tsx
// Loading não recebe props - é um componente simples
export default function Loading() {
return <div className="animate-pulse">Carregando...</div>;
}
// app/blog/error.tsx
"use client"; // error.tsx PRECISA ser client component
type ErrorProps = {
error: Error & { digest?: string };
reset: () => void;
};
export default function ErrorPage({ error, reset }: ErrorProps) {
return (
<div>
<h2>Algo deu errado</h2>
<p>{error.message}</p>
<button onClick={reset}>Tentar novamente</button>
</div>
);
}
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>404 - Página não encontrada</h2>
</div>
);
}
Server vs Client Components Tipados
// components/PostList.tsx - Server Component
// Sem 'use client' = roda no servidor por padrão
type Post = {
id: string;
title: string;
excerpt: string;
};
type PostListProps = {
category: string;
};
// Pode ser async! Server Components aceitam async
export default async function PostList({ category }: PostListProps) {
// Fetch direto no componente - sem useEffect
const posts: Post[] = await fetch(
`https://api.example.com/posts?category=${category}`,
{ next: { revalidate: 3600 } }
).then((r) => r.json());
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
// components/LikeButton.tsx - Client Component
"use client";
import { useState } from "react";
type LikeButtonProps = {
postId: string;
initialCount: number;
};
export function LikeButton({ postId, initialCount }: LikeButtonProps) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount((c) => c + 1)}>
{count} likes
</button>
);
}
Repare no padrão: Server Components são async e fazem fetch direto. Client Components usam hooks e eventos. Nunca misture os dois no mesmo arquivo. Quebre em componentes separados e componha na page.
Erros Comuns no Setup de TypeScript com App Router
Problemas que travam seu projeto
Desligar strict mode: Sem strict: true no tsconfig, TypeScript aceita null sem verificação, parâmetros implícitos any e vários padrões inseguros. Ligue strict e corrija os erros de uma vez.
Usar useState em Server Component: Server Components não aceitam hooks do React. Se precisa de estado, crie um Client Component separado. O TypeScript não pega esse erro sozinho, mas o Next.js explode em runtime.
Esquecer 'use client' no error.tsx: O error.tsx precisa ser Client Component porque usa o hook reset e pode ter estado. Sem 'use client', o Next.js dá erro obscuro no build.
Importar módulos do Node em Client Components: fs, path, crypto do Node não existem no browser. Se precisar dessas APIs, mova a lógica pra um Server Component ou route handler.
Não incluir .next/types no tsconfig: O Next.js gera tipos automáticos em .next/types. Se não tiver '**.next/types/**/*.ts' no include do tsconfig, perde typed routes e outros recursos.
Checklist de TypeScript no App Router
Construa um Projeto Completo com TypeScript + Next.js
Configurar TypeScript no App Router é o alicerce. No CrazyStack, você vai do setup até um SaaS completo: autenticação, dashboard, pagamentos, deploy. Cada componente, cada rota, cada API tipada do começo ao fim. O projeto é real e funciona em produção.
Se você quer dominar Next.js App Router com TypeScript de forma prática e sair com algo no ar, esse é o passo que falta.
Continue lendo
Como Tipar Page Params no Next.js App Router com TypeScript
Tipagem de params, searchParams e rotas dinâmicas no Next.js App Router.
Como Tipar Metadata no Next.js com TypeScript
Tipagem completa de Metadata e generateMetadata no Next.js com TypeScript.
Como Tipar Fetch e Data Fetching no Next.js com TypeScript
Typed fetch wrapper, data fetching em server components e cache tipado no Next.js.
Interface no TypeScript
Quando usar interface