Pular para o conteúdo
TypeScript

Tipar Layout no Next.js com TypeScript

Domine a tipagem de layouts no Next.js. De LayoutProps e children até parallel routes e metadata, tudo com exemplos que você aplica agora.

Por que isso é importante

Tipar Layout no Next.js com TypeScript. Domine a tipagem de layouts no Next.js. De LayoutProps e children até parallel routes e metadata, tudo com exemplos que você aplica agora.

O Que São Layouts e Templates no Next.js

No App Router do Next.js, layout.tsx é um componente que envolve as páginas de um segmento de rota. Ele recebe children como prop e renderiza ao redor de cada página. O detalhe: o layout NÃO remonta quando você navega entre páginas filhas. Ele persiste.

Já o template.tsx parece um layout, mas remonta a cada navegação. Isso é útil quando você quer animar transições de página ou resetar estado entre rotas.

O RootLayout é especial. Ele fica no app/layout.tsx e é obrigatório. Todo projeto Next.js precisa de um. Ele define as tags html e body, e envolve absolutamente tudo.

TypeScript entra pra garantir que esses componentes recebem as props certas. Se você esquece de declarar children, por exemplo, o layout compila mas renderiza vazio. Com tipagem explícita, esse erro vira um alerta no editor.

Como Tipar Layout e Template Passo a Passo

Vamos montar layouts tipados do zero. Cada passo cobre um cenário que você vai encontrar em projetos reais.

  1. Passo 1 - Tipando children: A prop children em layouts é sempre React.ReactNode. Declare explicitamente: { children: React.ReactNode }.
  2. Passo 2 - Usando o tipo do Next.js: O Next.js exporta tipos prontos. Você pode usar import type { Metadata } from 'next' pra tipar metadata e trabalhar com os tipos oficiais.
  3. Passo 3 - RootLayout com html e body: O RootLayout precisa retornar <html> e <body>. Tipe as props e garanta que children está dentro do body.
  4. Passo 4 - Layout com params: Layouts de rotas dinâmicas recebem params. Tipe como { params: Promise<{ slug: string }> } no Next.js 15+ ou { params: { slug: string } } em versões anteriores.
  5. Passo 5 - Parallel routes: Layouts podem receber slots nomeados como @modal, @sidebar. Cada slot é uma prop React.ReactNode adicional.
  6. Passo 6 - Metadata tipada: Exporte export const metadata: Metadata = { ... } pra ter autocomplete de todas as propriedades de SEO.

Exemplos Práticos de Layout Tipado

Código real que você copia e adapta pro seu projeto.

RootLayout Tipado Completo

// app/layout.tsx
import type { Metadata } from 'next';

// Metadata tipada com autocomplete completo
export const metadata: Metadata = {
  title: {
    default: 'Meu App',
    template: '%s | Meu App',
  },
  description: 'Descrição do app',
  openGraph: {
    type: 'website',
    locale: 'pt_BR',
  },
};

// Props do RootLayout
interface RootLayoutProps {
  children: React.ReactNode;
}

export default function RootLayout({ children }: RootLayoutProps) {
  return (
    <html lang="pt-BR">
      <body>
        {children}
      </body>
    </html>
  );
}

Layout com Params Dinâmicos

// app/blog/[slug]/layout.tsx

// Next.js 15+ usa Promise nos params
interface BlogLayoutProps {
  children: React.ReactNode;
  params: Promise<{ slug: string }>;
}

export default async function BlogLayout({
  children,
  params,
}: BlogLayoutProps) {
  const { slug } = await params;

  return (
    <div>
      <nav>Blog: {slug}</nav>
      <main>{children}</main>
    </div>
  );
}

// Versoes anteriores ao Next.js 15
// interface BlogLayoutProps {
//   children: React.ReactNode;
//   params: { slug: string };
// }

Layout com Parallel Routes (Slots)

// app/dashboard/layout.tsx
// Parallel routes: @analytics, @team, @notifications

interface DashboardLayoutProps {
  children: React.ReactNode;
  analytics: React.ReactNode;    // Slot @analytics
  team: React.ReactNode;          // Slot @team
  notifications: React.ReactNode; // Slot @notifications
}

export default function DashboardLayout({
  children,
  analytics,
  team,
  notifications,
}: DashboardLayoutProps) {
  return (
    <div className="grid grid-cols-12 gap-4">
      <aside className="col-span-3">
        {team}
        {notifications}
      </aside>
      <main className="col-span-6">{children}</main>
      <section className="col-span-3">{analytics}</section>
    </div>
  );
}

Template vs Layout: Diferença na Tipagem

// app/dashboard/template.tsx
// Template remonta a cada navegacao (diferente do layout)

interface DashboardTemplateProps {
  children: React.ReactNode;
}

export default function DashboardTemplate({
  children,
}: DashboardTemplateProps) {
  // Esse useEffect roda a cada navegacao
  // porque template remonta sempre
  return (
    <div className="animate-fadeIn">
      {children}
    </div>
  );
}

// A tipagem de props e identica ao layout
// A diferenca e no comportamento: template remonta, layout persiste

Metadata Dinâmica com generateMetadata

// app/blog/[slug]/layout.tsx
import type { Metadata } from 'next';

// generateMetadata recebe os mesmos params do layout
interface MetadataProps {
  params: Promise<{ slug: string }>;
}

export async function generateMetadata(
  { params }: MetadataProps
): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.date,
    },
  };
}

Em todos os exemplos, a tipagem pega erros antes de rodar. Se você esquecer de declarar um slot de parallel route ou passar o tipo errado em params, o editor avisa na hora.

Erros Comuns ao Tipar Layouts

Deslizes que geram bugs silenciosos

Esquecer de declarar children na interface: o layout compila, mas renderiza vazio. Sem children tipado, o TypeScript não reclama e você fica procurando o bug na rota errada.

Confundir layout com template: os dois recebem children, mas layout persiste e template remonta. Se você precisa de animação entre páginas, use template. Se precisa manter estado, use layout.

Tipar params como objeto direto no Next.js 15+: a partir do Next.js 15, params virou Promise. Se você tipar como objeto síncrono, o TypeScript compila mas o valor vem como Promise não resolvida.

Não tipar slots de parallel routes: cada pasta @nome na rota vira uma prop no layout. Se você não declara na interface, o slot é ignorado e a seção some da página.

Usar any em children: children deveria ser React.ReactNode. Usar any desliga a checagem e qualquer coisa passa sem aviso, incluindo valores que não são renderizáveis.

Checklist de Layout Tipado no Next.js

  • Children tipado como React.ReactNode em todos os layouts
  • RootLayout retorna tags html e body
  • Metadata exportada com tipo Metadata do Next.js
  • Params tipados como Promise no Next.js 15+
  • Slots de parallel routes declarados na interface de props
  • Template usado quando precisa remontar entre navegações
  • generateMetadata com retorno Promise
  • Nenhum any nas props de layout

Monte Layouts Profissionais na Prática

Layouts tipados são a base de qualquer aplicação Next.js séria. No CrazyStack, você constrói um projeto inteiro com App Router, layouts aninhados, parallel routes e metadata dinâmica. Tudo tipado com TypeScript do primeiro ao último arquivo.

Chega de layout quebrado em produção. Você termina o curso com uma aplicação completa rodando e pronta pra usar como portfólio.

Perguntas frequentes

O Que São Layouts e Templates no Next.js

No App Router do Next.js, layout.tsx é um componente que envolve as páginas de um segmento de rota. Ele recebe children como prop e renderiza ao redor de cada página. O detalhe: o layout NÃO remonta quando você navega entre páginas filhas. Ele persiste. Já o template.tsx parece um layout, mas remonta a cada navegação. Isso é útil quando você quer animar transições de página ou resetar estado entre rotas. O RootLayout é especial. Ele fica no app/layout.tsx e é obrigatório. Todo projeto Next.js precisa de um. Ele define as tags html e body, e envolve absolutamente tudo.

Como Tipar Layout e Template Passo a Passo

Vamos montar layouts tipados do zero. Cada passo cobre um cenário que você vai encontrar em projetos reais.