12
SEO, Sitemap, dan Deploy
Starter ini menyiapkan publikasi lewat beberapa bagian kecil yang saling bergantung: objek siteConfig, objek metadata di RootLayout, konfigurasi next-sitemap yang berjalan sebagai postbuild, dan satu header cache yang hanya berlaku di Vercel. Halaman ini menelusuri nilai dari sumbernya sampai ke berkas yang benar-benar dibaca mesin pencari dan peramban. Struktur layout serta metadata per route dibahas di App Router, Layout, dan Metadata.
Satu sumber, tiga jalur publikasi
graph TD
siteConfig["siteConfig (src/constant/config.ts)"] --> metadata["metadata di RootLayout"]
siteConfig --> ogImages["openGraph.images dan twitter.images"]
siteConfig --> robotsMeta["robots: index, follow"]
build["next build"] --> postbuild["postbuild: next-sitemap --config"]
sitemapCfg["siteUrl di next-sitemap.config.js"] --> postbuild
postbuild --> robotsTxt["robots.txt hasil generate"]
postbuild --> sitemapXml["sitemap.xml dan sitemap-*.xml"]
fontFace["@font-face di globals.css"] --> vercelJson["source di vercel.json"]
vercelJson --> fontCache["Cache-Control: immutable"]
style siteConfig fill:#f59e0b,color:#000
style sitemapCfg fill:#f59e0b,color:#000
style vercelJson fill:#f59e0b,color:#000
style build fill:#2d7d46,color:#fff
style postbuild fill:#2d7d46,color:#fff
style fontCache fill:#2d7d46,color:#fff
style metadata fill:#8b5cf6,color:#fff
style ogImages fill:#8b5cf6,color:#fff
style robotsMeta fill:#8b5cf6,color:#fff
style robotsTxt fill:#8b5cf6,color:#fff
style sitemapXml fill:#8b5cf6,color:#fff
style fontFace fill:#8b5cf6,color:#fff
%% fig: Jalur nilai dari siteConfig ke berkas publikasi
%% desc: siteConfig memasok metadata dan gambar OG, postbuild menurunkan sitemap serta robots.txt, dan vercel.json menambahkan header cache untuk berkas font.Tiga berkas menjadi sumber nilai: src/constant/config.ts, src/app/layout.tsx, dan next-sitemap.config.js. Ketiganya harus diubah bersamaan ketika domain atau judul situs berganti, karena tidak ada satu pun yang membaca nilai dari yang lain kecuali layout.tsx.
siteConfig sebagai sumber nilai
RootLayout mengimpor siteConfig lalu memakai ketiga field-nya secara langsung. Tidak ada pembacaan environment variable di jalur ini.
| Field | Nilai di repository | Dikonsumsi oleh |
|---|---|---|
title | Next.js + Tailwind CSS + TypeScript Starter | metadata.title.default, metadata.title.template, openGraph.title, openGraph.siteName, twitter.title |
description | A starter for Next.js, Tailwind CSS, and TypeScript with Absolute Import, Seo, Link component, pre-configured with Husky | metadata.description, openGraph.description, twitter.description |
url | https://tsnext-tw.thcl.dev | metadataBase, openGraph.url, openGraph.images, twitter.images |
⚠️ Komentar di berkas meminta
urlditulis tanpa/di akhir. Alasannya terlihat dilayout.tsx:imagesdibentuk dengan konkatenasi string${siteConfig.url}/images/og.jpg, jadi garis miring tambahan menghasilkan//images/og.jpg. src/constant/config.ts:1-71 src/app/layout.tsx:28-362
Metadata di RootLayout
Seluruh metadata situs didefinisikan sebagai satu objek Metadata yang diekspor dari RootLayout. Berikut pemetaan kunci ke nilainya.
| Kunci | Nilai | Efek |
|---|---|---|
metadataBase | new URL(siteConfig.url) | Dasar untuk URL relatif di metadata |
title.default | siteConfig.title | Judul halaman tanpa judul sendiri |
title.template | %s | ${siteConfig.title} | Judul halaman anak menjadi <judul> | <judul situs> |
description | siteConfig.description | Deskripsi default |
robots | { index: true, follow: true } | Instruksi crawl di dalam HTML |
icons.icon | /favicon/favicon.ico | Favicon utama |
icons.shortcut | /favicon/favicon-16x16.png | Shortcut icon |
icons.apple | /favicon/apple-touch-icon.png | Ikon iOS |
manifest | /favicon/site.webmanifest | Web app manifest |
openGraph | url, title, description, siteName, images, type: 'website', locale: 'en_US' | Kartu pratinjau Open Graph |
twitter | card: 'summary_large_image', title, description, images | Kartu pratinjau Twitter |
src/app/layout.tsx:8-193 src/app/layout.tsx:22-274 src/app/layout.tsx:28-362 src/app/layout.tsx:37-435
Dua hal mudah tertukar di sini. robots: { index: true, follow: true } adalah tag di HTML, bukan berkas robots.txt; berkas itu datang dari next-sitemap. Kedua, images di openGraph dan twitter menunjuk berkas statis public/images/og.jpg, bukan hasil helper openGraph di src/lib/og.ts.
Template judul bekerja pada halaman yang punya metadata sendiri. Layout komponen hanya menetapkan title: 'Components', sehingga judul akhirnya menjadi Components | Next.js + Tailwind CSS + TypeScript Starter. src/app/components/layout.tsx:6-96
Helper openGraph
openGraph adalah fungsi pembentuk URL untuk layanan gambar OG eksternal. Ia menerima siteName, description, templateTitle opsional, dan logo, lalu mengembalikan satu string. Setiap bagian di-encodeURIComponent, dan &templateTitle= hanya ditambahkan jika templateTitle diberikan. Nilai logo default masih placeholder: https://og.<your-domain>/images/logo.jpg. src/lib/og.ts:1-277
⚠️ Helper ini belum dipanggil dari halaman mana pun. Satu-satunya pemakai adalah test, yang memeriksa ada atau tidaknya
&templateTitle=pada hasil. Gantiog.<your-domain>sebelum memakainya. src/lib/tests/og.test.ts:3-19 — test itu sendiri dibahas di Pengujian dengan Jest.
Sitemap dan robots.txt setelah build
package.json mendaftarkan postbuild yang menjalankan next-sitemap --config next-sitemap.config.js. Skrip postX dijalankan otomatis oleh npm dan pnpm setelah build, jadi pnpm build menghasilkan sitemap tanpa langkah tambahan. package.json:178
Konfigurasi next-sitemap hanya memuat siteUrl, generateRobotsTxt: true, dan satu policy yang mengizinkan seluruh path. next-sitemap.config.js:5-139
| Berkas | Sumber | Isi | Dilacak git |
|---|---|---|---|
sitemap.xml | postbuild | Daftar URL dari siteUrl | Tidak |
robots.txt | postbuild | generateRobotsTxt: true dengan policy userAgent: '*', allow: '/' | Tidak |
sitemap-*.xml | postbuild | Pecahan sitemap bila daftar URL dipecah | Tidak |
.gitignore:39-42
⚠️
siteUrlditulis dua kali: sebagaiurldisiteConfigdan sebagaisiteUrldinext-sitemap.config.js. Keduanya bernilai sama sekarang, tetapi mengganti domain hanya di satu tempat membuat metadata dan sitemap menunjuk host berbeda. next-sitemap.config.js:5-139
Aset ikon dan manifest
Metadata ikon menunjuk berkas yang tersedia di public/favicon. Semua berkas itu sudah ada di repository.
| Kunci metadata | Berkas |
|---|---|
icons.icon | public/favicon/favicon.ico |
icons.shortcut | public/favicon/favicon-16x16.png |
icons.apple | public/favicon/apple-touch-icon.png |
manifest | public/favicon/site.webmanifest |
icons di manifest | android-chrome-192x192.png, android-chrome-512x512.png |
src/app/layout.tsx:22-274 public/favicon/site.webmanifest:1-1910
⚠️ Di manifest,
"name"dan"short_name"masih string kosong. Keduanya dipakai sebagai nama aplikasi saat pengguna memasang situs ke home screen, jadi isi dulu sebelum publikasi.theme_colordanbackground_colorsudah bernilai#ffffff. public/favicon/site.webmanifest:1-1910
Header cache font di vercel.json
vercel.json menyimpan satu aturan headers: untuk source /fonts/inter-var-latin.woff2, response diberi Cache-Control: public, max-age=31536000, immutable. Path itu sama dengan src di blok @font-face pada globals.css, jadi yang di-cache adalah berkas yang benar-benar dimuat halaman. vercel.json:1-1311 src/styles/globals.css:66-7112
sequenceDiagram
participant B as Browser
participant V as Vercel edge
participant A as Berkas statis
B->>V: GET /fonts/inter-var-latin.woff2
V->>A: read asset
A-->>V: woff2 bytes
V-->>B: 200 plus Cache-Control: public, max-age=31536000, immutable
Note over B: permintaan berikutnya dilayani cache lokal
%% fig: Siklus satu request font dengan header dari vercel.json
%% desc: Aturan headers di vercel.json menempelkan Cache-Control berumur satu tahun pada berkas font, sehingga peramban tidak meminta ulang.@font-face juga memakai font-display: block dan font-weight: 100 900, sehingga satu berkas variabel melayani seluruh rentang bobot. Header cache ini hanya berlaku jika aplikasi di-deploy ke Vercel; platform lain mengabaikan vercel.json.
Deploy
README menempatkan deploy ke Vercel sebagai langkah keempat setelah inisialisasi proyek, lengkap dengan tombol Deploy with Vercel. README.md:73-7513
Tombol yang sama muncul di halaman beranda. UnstyledLink mengarah ke https://vercel.com/new/git/external?repository-url=... dan memuat gambar https://vercel.com/button dengan alt='Deploy with Vercel'. src/app/page.tsx:45-5614
| Langkah | Perintah atau berkas | Catatan |
|---|---|---|
| Build | pnpm build | Menjalankan next build lalu postbuild |
| Sitemap | next-sitemap.config.js | siteUrl wajib sama dengan siteConfig.url |
| Header cache | vercel.json | Hanya berpengaruh di Vercel |
| Direktori lokal | .vercel | Diabaikan git |
.gitignore:32-33
⚠️ Berkas
sitemap.xmldanrobots.txttidak masuk repository. Deploy yang dijalankan tanpabuildpenuh, misalnya hanya mengunggah hasil yang sudah ada, akan tampil tanpa sitemap. Otomasitest,lint, dantypechecksebelumcommitada di Otomasi Repository: Husky, Commitlint, dan CI.
Di atas ketiga lapisan ini, tidak ada konfigurasi deploy lain di repository: tidak ada file platform selain vercel.json, dan tidak ada workflow yang melakukan deploy otomatis. Publikasi sepenuhnya bergantung pada build, postbuild, dan setelan proyek di Vercel.