6
Halaman, Error Boundary, dan Endpoint API
src/app pada commit ini berisi lima berkas yang bersama-sama membentuk permukaan aplikasi: halaman /, katalog /components, error.tsx sebagai error boundary, not-found.tsx sebagai halaman rute tak dikenal, dan Route Handler api/hello. Empat berkas pertama berupa komponen React, yang terakhir tidak me-render UI sama sekali.
Peta rute dan jenis komponen
Setiap rute dipetakan langsung oleh nama berkas di dalam src/app. Tabel berikut mencatat jenis komponen dan keluaran yang dihasilkan tiap berkas.
| URL | Berkas | Jenis | Keluaran | Rujukan |
|---|---|---|---|---|
/ | src/app/page.tsx | Client Component, ditandai 'use client' | halaman sambutan dan tautan ke /components | src/app/page.tsx:1-41 |
/components | src/app/components/page.tsx | Client Component yang menyimpan mode dan color di React.useState | katalog komponen pratinjau | src/app/components/page.tsx:27-322 |
/api/hello | src/app/api/hello/route.ts | Route Handler yang hanya mengekspor GET | NextResponse.json({ hello: 'Next.js' }) | src/app/api/hello/route.ts:1-53 |
| error segmen | src/app/error.tsx | Client Component, wajib | teks "Oops, something went wrong!" dan tombol reset | src/app/error.tsx:1-184 |
| rute tak dikenal | src/app/not-found.tsx | Server Component yang mengekspor metadata | teks "Page Not Found" | src/app/not-found.tsx:1-75 |
Kedua halaman yang dilihat pengguna berjalan di client. src/app/page.tsx bahkan mengimpor @/lib/env di baris keempat, sehingga envVariables.parse(process.env) ikut dijalankan saat modul halaman dimuat. Rincian validasi itu ada di Utilitas, Logger, dan Environment.
Di src/app tidak ada loading.tsx, template.tsx, maupun global-error.tsx. Konsekuensinya error.tsx hanya menangkap error dari segmen di bawah RootLayout, bukan error yang dilempar RootLayout sendiri. Susunan layout dan metadata dibahas di App Router, Layout, dan Metadata.
Halaman utama /
HomePage menyusun satu <main> berisi satu <section className='bg-white'> dengan pembungkus layout relative flex min-h-screen flex-col items-center justify-center py-12 text-center src/app/page.tsx:24-436. Isinya berurutan: komponen Logo hasil impor ~/svg/Logo.svg, <h1> berisi "Next.js + Tailwind CSS + TypeScript Starter", ArrowLink ke repository GitHub, dan ButtonLink dengan href='/components' serta variant='light' yang menjadi pintu masuk ke katalog. Transformasi *.svg menjadi komponen React diatur @svgr/webpack, yang dibahas di Konfigurasi Build dan Alat Pengembangan.
Dua tautan keluar melengkapi halaman. Tombol deploy Vercel ditulis sebagai <img> biasa di dalam UnstyledLink dengan width='92' dan height='32', dan baris eslint-disable-next-line @next/next/no-img-element menandai penyimpangan itu src/app/page.tsx:45-567. Footer memakai new Date().getFullYear() dan UnderlineLink ke https://theodorusclarence.com?ref=tsnextstarter src/app/page.tsx:58-638. Karena halaman ini client component, tahun pada footer dihitung di browser, bukan saat build.
Katalog pratinjau /components
ComponentPage menyimpan dua state: mode bertipe 'dark' | 'light' dengan nilai awal 'light', dan color bertipe turunan colorList dengan nilai awal 'sky' src/app/components/page.tsx:27-322. colorList sendiri berisi 22 nama warna Tailwind, dari 'slate' sampai 'rose', dan ditutup as const supaya tipenya menyempit src/app/components/page.tsx:438-4619.
Navigasi keluar dari katalog ada dua arah. ArrowLink direction='left' href='/' mengembalikan pengunjung ke halaman utama src/app/components/page.tsx:48-5010, sedangkan ButtonLink href='/404' menawarkan halaman 404 kustom src/app/components/page.tsx:402-40811. Isi tiap komponen yang dipamerkan dijelaskan di Katalog Komponen Pratinjau.
⚠️
ButtonLink href='/404'tidak punya berkas rute yang cocok.src/apptidak berisi direktori404dan repository ini tidak punya direktorisrc/pages, sehinggarequestke/404dilayaninot-found.tsx— halaman yang sama dengan rute tak dikenal lain. Tautan itu menampilkan "Page Not Found", bukan halaman kustom tersendiri src/app/not-found.tsx:9-2312.
Endpoint /api/hello
Route Handler ini adalah satu-satunya endpoint di repository. Berkasnya hanya berisi lima baris.
| Aspek | Nilai | Rujukan |
|---|---|---|
| Berkas rute | src/app/api/hello/route.ts | src/app/api/hello/route.ts:1-53 |
| Metode yang diekspor | GET, sebagai async function tanpa parameter | src/app/api/hello/route.ts:3-513 |
| Nilai balik | NextResponse.json({ hello: 'Next.js' }) | src/app/api/hello/route.ts:414 |
| Pemakai di dalam repository | tidak ada, tidak satu pun berkas lain memanggil /api/hello | src/app/api/hello/route.ts:1-53 |
Karena hanya GET yang diekspor, endpoint ini tidak menyediakan POST, PUT, atau DELETE. Tidak ada autentikasi, pembacaan request, maupun cache khusus; response JSON dikirim apa adanya. Endpoint ini berfungsi sebagai contoh bentuk Route Handler, bukan sebagai backend aplikasi.
Perpindahan antara halaman normal, error boundary, dan halaman tidak ditemukan
Diagram berikut merangkum transisi yang benar-benar dibuka oleh kode: tautan antahalaman, rute yang tidak terdaftar, dan reset dari error boundary.
stateDiagram-v2
[*] --> Home
Home --> Components: ButtonLink href="/components"
Components --> Home: ArrowLink direction="left"
Home --> NotFound: rute tidak terdaftar
Components --> NotFound: ButtonLink href="/404"
NotFound --> Home: a href="/"
Home --> ErrorBoundary: render melempar Error
Components --> ErrorBoundary: render melempar Error
ErrorBoundary --> Home: reset() dari TextButton
classDef ok fill:#2d7d46,color:#fff
classDef bad fill:#dc2626,color:#fff
classDef warn fill:#f59e0b,color:#000
class Home,Components ok
class ErrorBoundary bad
class NotFound warn
%% fig: Transisi halaman normal, error boundary, dan 404
%% desc: Dua halaman client dapat saling bertukar lewat tautan, berakhir di not-found.tsx saat rute tidak ada, atau masuk ke error.tsx yang menyediakan reset.Kedua penanganan kegagalan memakai ikon dan gaya yang sama, tetapi kontraknya berbeda.
| Aspek | src/app/error.tsx | src/app/not-found.tsx |
|---|---|---|
| Direktif | 'use client' dengan komentar // Error components must be Client Components src/app/error.tsx:115 | tanpa direktif, jadi Server Component src/app/not-found.tsx:1-75 |
| Props | error: Error & { digest?: string } dan reset: () => void src/app/error.tsx:8-1416 | tidak menerima props src/app/not-found.tsx:917 |
| Efek samping | console.error(error) di dalam React.useEffect dengan dependensi [error] src/app/error.tsx:15-1818 | tidak ada |
| Aksi pemulihan | TextButton variant='basic' onClick={reset} berlabel "Try again" src/app/error.tsx:31-3319 | tautan <a href='/'> berlabel "Back to home" src/app/not-found.tsx:1920 |
| Metadata | tidak diekspor | metadata dengan title: 'Not Found' src/app/not-found.tsx:5-721 |
Tombol "Try again" pada error boundary adalah TextButton, yang me-render <button type='button'> sehingga reset terpasang sebagai handler onClick biasa src/components/buttons/TextButton.tsx:22-2622. Judul 'Not Found' dari not-found.tsx masuk ke template %s | ${siteConfig.title} milik RootLayout, jadi tab browser menampilkan "Not Found" diikuti judul situs src/app/layout.tsx:12-1723. Sebaliknya error.tsx tidak mengatur judul, karena komponen error tidak boleh mengekspor metadata.