Capítulo 119 de 456
layout.js define UI compartilhada entre múltiplas rotas, sendo o componente mais externo de um segmento na hierarquia (envolve template.js, error.js, loading.js, not-found.js e page.js). Layouts não re-renderizam em navegação, o que traz restrições importantes de acesso a dado runtime.
children (obrigatório): populado com o segmento filho (layout, page, ou special file).params (opcional): Promise com os dynamic route params do root até o layout; requer await/use().LayoutProps<'/route'>: helper de tipagem global (gerado por next dev/next build/next typegen) que infere params e named slots (@analytics) tipados a partir da estrutura de diretório.app/layout.js; único lugar que define <html>/<body>; nunca adicione <head> manual, use a Metadata API.layout.js acima dele é um root layout; criados via route groups (app/(shop)/layout.js) ou omitindo app/layout.js em subdiretórios; navegar entre root layouts diferentes causa full page load, não client-side navigation.app/[lang]/layout.js), lido de qualquer Server Component via next/root-params.export default async function Layout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
}
params (Promise) em um layout de segmento dinâmico.import { Suspense } from 'react'
import { NavSkeleton } from './nav-skeleton'
import { DashboardNav } from './dashboard-nav'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<Suspense fallback={<NavSkeleton />}>
<DashboardNav />
</Suspense>
<main>{children}</main>
</>
)
}
<Suspense> próprio, já que loading.js (abaixo do layout na hierarquia) não cobre acesso a dado uncached/runtime dentro do próprio layout.'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}
useSearchParams() num Client Component filho.| Example Route | URL | params |
|---|---|---|
app/dashboard/[team]/layout.js | /dashboard/1 | Promise<{ team: '1' }> |
app/shop/[tag]/[item]/layout.js | /shop/1/2 | Promise<{ tag: '1', item: '2' }> |
app/blog/[...slug]/layout.js | /blog/1/2 | Promise<{ slug: ['1', '2'] }> |
<head> manual (<title>, <meta>) no root layout: use a Metadata API, que já lida com streaming e deduplicação.cookies()/headers() cru no layout sem await cookies()/await headers() explícito: layout não tem request object direto; use as APIs cookies/headers em Server Components/Functions.searchParams da Page ou useSearchParams() num Client Component.usePathname() num Client Component.<Suspense> próprio: sem Cache Components, bloqueia a navegação inteira até o layout terminar; com Cache Components, gera erro de build orientando a envolver em <Suspense>.children via prop: não é possível; refetch (deduped por fetch/React.cache) é o padrão aceito.app/layout.js), mas cruzar entre eles força full page load.loading.js não cobre dado uncached acessado dentro do próprio layout.js (ele fica abaixo na hierarquia); envolva esse acesso em <Suspense> no layout ou mova para page.js.LayoutProps<'/route'> dá tipagem automática de params e slots nomeados, gerada por next dev/build/typegen.params compartilhada.