Pular para o conteúdo
TypeScript

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.

  1. Passo 1 - Crie o projeto com TypeScript: Rode npx create-next-app@latest meu-projeto --typescript --app. O flag --app garante App Router. O --typescript configura tudo automático.
  2. Passo 2 - Verifique o tsconfig.json: Confirme que strict está true. Sem strict, TypeScript é frouxo demais. Confira também que moduleResolution é 'bundler' e que paths tem o alias '@/*' configurado.
  3. Passo 3 - Configure o next.config.ts: Sim, .ts e não .js. O Next.js 15+ suporta config em TypeScript nativo. Importe o tipo NextConfig e tipe o objeto de configuração.
  4. Passo 4 - Estruture os arquivos especiais: Crie page.tsx, layout.tsx, loading.tsx, error.tsx e not-found.tsx com as assinaturas corretas. Cada um tem props e retorno específicos.
  5. Passo 5 - Separe Server e Client Components: Por padrão, tudo é Server Component no App Router. Marque com 'use client' apenas componentes que usam hooks, eventos ou browser APIs.
  6. Passo 6 - Configure path aliases: O @ já mapeia pra src/ por padrão. Se precisar de mais aliases, adicione no tsconfig.json em compilerOptions.paths e no next.config.ts.

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 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 => 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

  • Projeto criado com --typescript e --app flags
  • tsconfig.json com strict: true ativado
  • next.config.ts (não .js) com tipo NextConfig
  • Layout raiz com props { children: ReactNode } tipado
  • Pages com params e searchParams como Promise (Next.js 15+)
  • error.tsx com 'use client' e props { error, reset } tipadas
  • Server Components async pra data fetching direto
  • Client Components separados com 'use client' pra hooks e eventos
  • .next/types incluído no tsconfig pra typed routes

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.