5
App Router, Layout, dan Metadata
src/app memakai App Router. Di akar segmen hanya ada satu layout.tsx; satu layout tambahan duduk di src/app/components/ bersama page.tsx milik segmen itu. Root layout memasang stylesheet global sekaligus mendefinisikan metadata dasar, sedangkan layout components menimpa judul untuk segmen /components. src/app/layout.tsx:1-81 src/app/components/layout.tsx:1-172
Peta route, layout, dan sumber metadata
Hanya tiga berkas di src/app yang mengekspor metadata: root layout, layout components, dan not-found. Berkas page.tsx sendiri tidak mendeklarasikan metadata apa pun, sehingga judulnya diwarisi dari layout terdekat. src/app/page.tsx:24-283 src/app/not-found.tsx:5-74
| Route | Berkas route | Layout yang membungkus | Asal metadata |
|---|---|---|---|
/ | src/app/page.tsx:24-283 | RootLayout | metadata di src/app/layout.tsx:12-505 |
/components | src/app/components/page.tsx:276 | RootLayout, lalu ComponentsLayout | metadata di src/app/components/layout.tsx:6-97 |
| route yang tidak cocok | src/app/not-found.tsx:9-208 | RootLayout | metadata di src/app/not-found.tsx:5-74 |
Halaman not-found dibahas lebih jauh di Halaman, Error Boundary, dan Endpoint API.
graph TD
siteConfig["siteConfig (src/constant/config.ts)"]
metaRoot["metadata (src/app/layout.tsx)"]
rootLayout["RootLayout (src/app/layout.tsx)"]
doc["<html> dan <body>"
homePage["/ - src/app/page.tsx"]
metaComponents["metadata title Components"]
componentsLayout["ComponentsLayout (src/app/components/layout.tsx)"]
componentsPage["/components - src/app/components/page.tsx"]
siteConfig --> metaRoot
metaRoot --> rootLayout
rootLayout --> doc
doc --> homePage
doc --> componentsLayout
metaComponents --> componentsLayout
componentsLayout --> componentsPage
style siteConfig fill:#f59e0b,color:#000
style metaRoot fill:#f59e0b,color:#000
style rootLayout fill:#2d7d46,color:#fff
style doc fill:#2d7d46,color:#fff
style metaComponents fill:#8b5cf6,color:#fff
style componentsLayout fill:#8b5cf6,color:#fff
%% fig: Pewarisan layout dan metadata ke tiap route
%% desc: siteConfig mengisi metadata root, RootLayout merender html dan body, lalu segmen components menambahkan layout dan metadata sendiri.RootLayout: satu-satunya tempat stylesheet global dipasang
RootLayout mengimpor @/styles/globals.css pada baris keempat dan @/styles/colors.css pada baris keenam. Komentar !STARTERCONF di atas impor kedua menyatakan berkas warna itu hanya untuk demo. src/app/layout.tsx:4-69
Isi globals.css yang dipakai seluruh halaman terbagi tiga blok. Blok @theme mendefinisikan --font-primary, palet --color-primary-50 sampai --color-primary-950, --color-dark, serta --animate-flicker dan --animate-shimmer. src/styles/globals.css:1-2710 Dua keyframe yang dirujuk token animasi itu, flicker dan shimmer, dideklarasikan sesudahnya. src/styles/globals.css:30-6211 @layer base memuat @font-face Inter dari /fonts/inter-var-latin.woff2, kelas tipografi .h0 sampai .h4 beserta body dan .p, kelas .layout dengan max-width: 68.75rem, dan .min-h-main. src/styles/globals.css:64-12612
Kelas .layout itu yang dipakai halaman beranda pada elemen pembungkusnya, sehingga lebar konten konsisten di semua halaman. src/app/page.tsx:24-283 Rincian token warna dan token animasi ada di Styling, Token Warna, dan Tema.
⚠️
RootLayoutmerender<html>tanpa atributlangdan<body>tanpaclassName. Kalau kamu menambahkanclassNameglobal, pasang dibodypada berkas ini, bukan diglobals.css, karenaglobals.csssudah diimpor di sini dan tidak di tempat lain. src/app/layout.tsx:52-6213
Metadata dasar yang dibangun dari siteConfig
Nilai metadata di root layout hampir seluruhnya berasal dari siteConfig, yang hanya berisi tiga field teks. src/constant/config.ts:1-714
Field metadata | Nilai | Baris |
|---|---|---|
metadataBase | new URL(siteConfig.url) | src/app/layout.tsx:12-1315 |
title.default | siteConfig.title | src/app/layout.tsx:14-1716 |
title.template | ``%s | ${siteConfig.title}`` |
description | siteConfig.description | src/app/layout.tsx:1817 |
robots | { index: true, follow: true } | src/app/layout.tsx:1918 |
icons | /favicon/favicon.ico, /favicon/favicon-16x16.png, /favicon/apple-touch-icon.png | src/app/layout.tsx:22-2619 |
manifest | /favicon/site.webmanifest | src/app/layout.tsx:2720 |
openGraph | url, title, description, siteName, type: 'website', locale: 'en_US' | src/app/layout.tsx:28-3621 |
openGraph.images | ${siteConfig.url}/images/og.jpg | src/app/layout.tsx:3322 |
twitter | card: 'summary_large_image' plus judul, deskripsi, dan gambar yang sama | src/app/layout.tsx:37-4323 |
Dua bagian dinonaktifkan lewat komentar: twitter.creator dan seluruh array authors. Keduanya perlu dibuka kembali kalau kamu ingin atribut itu muncul di <head>. src/app/layout.tsx:42-4924
Judul halaman anak menimpa template
title.template di root layout berbentuk %s | ${siteConfig.title}, jadi judul yang dideklarasikan segmen anak masuk ke slot %s. Layout components mendeklarasikan title: 'Components' dan description: 'Pre-built components with awesome default', sehingga rute /components memakai judul turunan itu, bukan judul default. src/app/components/layout.tsx:6-97
Layout components juga mengimpor @/styles/colors.css sekali lagi, padahal root layout sudah mengimpornya. Impor ganda ini tidak mengubah hasil, tetapi komentar !STARTERCONF di root layout tetap menyarankan penghapusan berkas warna demo tersebut. src/app/components/layout.tsx:1-425
sequenceDiagram
participant B as Browser
participant R as RootLayout
participant S as siteConfig
participant C as ComponentsLayout
B->>R: GET /components
R->>S: baca url, title, description
R->>R: pasang title.default dan title.template
R->>C: render children
C->>C: metadata.title = "Components"
C-->>B: head berisi "Components | Next.js + Tailwind CSS + TypeScript Starter"
%% fig: Resolusi metadata satu request ke /components
%% desc: Root layout menyusun judul default dan template dari siteConfig, lalu layout components mengisi slot judul untuk segmennya.Satu hal yang perlu diingat: layout anak tidak mengganti seluruh objek metadata, ia hanya menimpa field yang dideklarasikannya. Karena components/layout.tsx hanya menulis title dan description, field seperti icons, manifest, dan openGraph tetap diwarisi dari root layout. src/app/components/layout.tsx:6-97 src/app/layout.tsx:12-505
Menyesuaikan untuk proyek sendiri
Ganti url, title, dan description di siteConfig lebih dulu; tiga field itu yang mengalir ke metadataBase, judul, deskripsi, openGraph, dan twitter. src/constant/config.ts:1-714 Setelah itu sesuaikan icons dan manifest, yang menunjuk berkas di direktori /favicon. Langkah deploy dan berkas SEO lain ada di SEO, Sitemap, dan Deploy.