kodingup.camp

Beranda/Next.js Mastery: Dasar, Routing & Rendering

Routing Berbasis File

Modul 3 dari 104 menit bacaFile-based Routing

Isi modul

    Kenapa ini penting

    Di React + Vite kamu harus memasang React Router dan menulis daftar rute secara manual. Di Next.js, struktur folder adalah daftar rute. Kamu membuat folder, menaruh file page.js, dan URL-nya langsung ada. Cara ini membuat proyek besar tetap mudah dinavigasi: kalau mau mencari kode halaman /blog/kategori, kamu tahu persis foldernya.

    Konsep inti

    Folder = segmen URL, page.js = halaman

    Analoginya seperti gedung perkantoran. Folder adalah lantai dan ruangan, sedangkan page.js adalah papan nama di pintu. Ruangan tanpa papan nama tidak bisa dikunjungi tamu.

    File URL
    app/page.js /
    app/tentang/page.js /tentang
    app/blog/page.js /blog
    app/blog/kategori/page.js /blog/kategori
    app/blog/_components/Kartu.js tidak bisa diakses (folder privat)

    Aturan penting:

    layout.js: UI yang dipakai bersama

    layout.js membungkus semua halaman di folder itu dan subfoldernya lewat prop children. Saat user berpindah halaman, layout tidak di-render ulang dan state-nya tetap. Cocok untuk navbar, sidebar, dan footer. Detailnya dibahas di modul Layout.

    Jangan pakai <a href> biasa untuk link internal. Pakai komponen Link dari next/link:

    Untuk link ke situs luar, <a href="https://..."> biasa tetap tepat.

    useRouter dan usePathname

    Dua hook dari next/navigation (bukan next/router, itu milik Pages Router). Keduanya hanya bisa dipakai di Client Component (file dengan 'use client' di baris pertama; dibahas lengkap di modul 6).

    Aturan praktis: kalau bisa pakai <Link>, pakai <Link>. useRouter untuk kasus "pindah halaman setelah sesuatu terjadi", misalnya setelah form berhasil dikirim.

    Route group: (nama)

    Kadang kamu ingin mengelompokkan halaman tanpa mengubah URL. Bungkus nama folder dengan tanda kurung: app/(marketing)/tentang/page.js tetap menjadi /tentang. Kegunaannya:

    Hati-hati: dua group tidak boleh menghasilkan URL yang sama, misalnya (marketing)/tentang/page.js dan (toko)/tentang/page.js akan bentrok.

    Halaman 404: not-found.js

    File app/not-found.js menampilkan UI saat URL tidak cocok dengan rute mana pun. Kamu juga bisa memanggil fungsi notFound() dari next/navigation di dalam halaman untuk memicu 404 secara manual, misalnya saat artikel tidak ditemukan di database. Kita pakai ini di modul berikutnya.

    Contoh kode

    Struktur yang akan kita bangun:

    app/
    ├── layout.js
    ├── page.js                 → /
    ├── not-found.js            → UI 404
    ├── (marketing)/
    │   ├── layout.js           → layout khusus marketing
    │   └── tentang/page.js     → /tentang
    ├── blog/
    │   ├── layout.js           → navbar blog
    │   └── page.js             → /blog
    └── components/              (atau di luar app/, misalnya /components)
    

    Komponen navigasi dengan penanda menu aktif. Karena memakai hook, file ini harus Client Component:

    // components/NavLink.jsx
    'use client'
    
    import Link from 'next/link'
    import { usePathname } from 'next/navigation'
    
    export default function NavLink({ href, children }) {
      const pathname = usePathname() // → misalnya '/blog/halo-nextjs'
      const aktif = pathname === href || pathname.startsWith(href + '/')
    
      return (
        <Link
          href={href}
          className={aktif ? 'font-bold text-blue-600' : 'text-gray-600 hover:text-gray-900'}
        >
          {children}
        </Link>
      )
    }
    

    Layout blog memakai NavLink. Layout sendiri tetap Server Component:

    // app/blog/layout.jsx
    import NavLink from '@/components/NavLink'
    
    export default function BlogLayout({ children }) {
      return (
        <section className="mx-auto max-w-2xl p-6">
          <nav className="mb-6 flex gap-4">
            <NavLink href="/blog">Semua artikel</NavLink>
            <NavLink href="/tentang">Tentang</NavLink>
          </nav>
          {children}
        </section>
      )
    }
    // → di /blog, menu "Semua artikel" tebal biru; di /tentang, menu "Tentang" yang aktif
    

    Halaman daftar artikel dengan <Link>:

    // app/blog/page.jsx
    import Link from 'next/link'
    
    const posts = [
      { slug: 'halo-nextjs', judul: 'Halo, Next.js!' },
      { slug: 'routing-itu-folder', judul: 'Routing Itu Folder' },
    ]
    
    export default function BlogPage() {
      return (
        <ul className="space-y-2">
          {posts.map((post) => (
            <li key={post.slug}>
              <Link href={`/blog/${post.slug}`} className="underline">
                {post.judul}
              </Link>
            </li>
          ))}
        </ul>
      )
    }
    // → klik link: URL berubah ke /blog/halo-nextjs tanpa reload penuh
    

    Navigasi lewat kode dengan useRouter:

    // components/TombolKembali.jsx
    'use client'
    
    import { useRouter } from 'next/navigation' // BUKAN 'next/router'
    
    export default function TombolKembali() {
      const router = useRouter()
      return (
        <div className="flex gap-2">
          <button onClick={() => router.back()}>← Kembali</button>
          <button onClick={() => router.push('/blog')}>Ke daftar blog</button>
        </div>
      )
    }
    

    Route group dengan layout sendiri, dan halaman 404 kustom:

    // app/(marketing)/layout.jsx, hanya membungkus halaman di dalam (marketing)
    export default function MarketingLayout({ children }) {
      return <div className="bg-amber-50 p-6">{children}</div>
    }
    
    // app/(marketing)/tentang/page.jsx, URL-nya /tentang, bukan /marketing/tentang
    export default function TentangPage() {
      return <h1 className="text-2xl font-bold">Tentang kami</h1>
    }
    
    // app/not-found.jsx
    import Link from 'next/link'
    
    export default function NotFound() {
      return (
        <main className="p-6">
          <h1 className="text-2xl font-bold">404 - Halaman tidak ada</h1>
          <Link href="/" className="underline">Kembali ke beranda</Link>
        </main>
      )
    }
    // → buka /halaman-ngawur → status 404 dan UI di atas tampil
    

    Kesalahan umum

    1. Import useRouter dari next/router. Itu API Pages Router. Di App Router pakai import { useRouter } from 'next/navigation'.
    2. Memakai hook di Server Component. usePathname dan useRouter hanya jalan di file dengan 'use client'. Kalau lupa, muncul error yang menyuruh menambahkan directive tersebut.
    3. Membuat folder tanpa page.js lalu heran kenapa 404. Folder saja tidak cukup; harus ada page.js di dalamnya.
    4. Memakai <a href="/blog"> untuk link internal. Halaman akan reload penuh, state hilang, dan tidak ada prefetch. Pakai <Link>.
    5. Mengira route group masuk ke URL. (marketing) tidak muncul di URL. Kalau mau segmen URL sungguhan, pakai folder biasa tanpa kurung.

    Latihan

    1. Mudah, Buat halaman /kontak yang menampilkan alamat email, lalu tambahkan link ke halaman itu dari halaman utama memakai <Link>.
    2. Sedang, Buat route group (toko) berisi /menu dan /keranjang dengan layout yang punya header "Kedai Kopi". Halaman /tentang di luar group tidak boleh memakai header itu.
    3. Menantang, Buat komponen Breadcrumb (Client Component) yang membaca usePathname() dan menampilkan jejak seperti Beranda / blog / halo-nextjs, dengan tiap bagian berupa <Link> ke path-nya.
    Lihat solusi

    1.

    // app/kontak/page.jsx
    export default function KontakPage() {
      return <p>Email kami: halo@kedaikopi.id</p>
    }
    
    // app/page.jsx
    import Link from 'next/link'
    
    export default function HomePage() {
      return (
        <main className="p-6">
          <h1>Kedai Kopi</h1>
          <Link href="/kontak" className="underline">Hubungi kami</Link>
        </main>
      )
    }
    

    2.

    app/
    ├── (toko)/
    │   ├── layout.jsx
    │   ├── menu/page.jsx       → /menu
    │   └── keranjang/page.jsx  → /keranjang
    └── tentang/page.jsx        → /tentang (tanpa header toko)
    
    // app/(toko)/layout.jsx
    export default function TokoLayout({ children }) {
      return (
        <>
          <header className="bg-amber-900 p-4 text-white">Kedai Kopi</header>
          <main className="p-6">{children}</main>
        </>
      )
    }
    
    // app/(toko)/menu/page.jsx
    export default function MenuPage() {
      return <h1>Menu hari ini</h1>
    }
    
    // app/(toko)/keranjang/page.jsx
    export default function KeranjangPage() {
      return <h1>Keranjang kamu kosong</h1>
    }
    

    3.

    // components/Breadcrumb.jsx
    'use client'
    
    import Link from 'next/link'
    import { usePathname } from 'next/navigation'
    
    export default function Breadcrumb() {
      const pathname = usePathname()                 // → '/blog/halo-nextjs'
      const bagian = pathname.split('/').filter(Boolean) // → ['blog', 'halo-nextjs']
    
      return (
        <nav className="text-sm text-gray-500">
          <Link href="/">Beranda</Link>
          {bagian.map((nama, i) => {
            const href = '/' + bagian.slice(0, i + 1).join('/')
            return (
              <span key={href}>
                {' / '}
                <Link href={href}>{nama}</Link>
              </span>
            )
          })}
        </nav>
      )
    }
    // → Beranda / blog / halo-nextjs
    

    Ringkasan

    Video pendamping

    Kalau penjelasan di atas belum masuk, tonton ini. Video dari kanal YouTube pihak lain.

    Next.js 15 Tutorial - 5 - RoutingCodevolution · Bahasa InggrisBuka di YouTube

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.