Metadata SEO Next.js App Router: Canonical, Hreflang, JSON-LD
Satu helper pageMetadata plus metadataBase dan JSON-LD LocalBusiness menutup canonical, hreflang, dan kartu sosial untuk seluruh route Next.js.
Ringkasan
Jadi gitu, satu commit soal metadata bikin 16 route bilingual lolos audit SEO tanpa temuan. Ternyata rahasianya tiga lapis: canonical dan hreflang yang saling nunjuk, metadataBase buat rapiin URL, plus JSON-LD biar Google tahu ini bisnis beneran. Semuanya dikonsolidasi ke satu helper, jadi nambah route baru tinggal panggil fungsi, beres.
Enam belas route lolos audit dalam satu commit
Layar terminal menampilkan output yang jarang saya lihat: enam belas route bilingual lewat audit SEO internal tanpa satu pun temuan. Pemicunya satu commit berisi seo: complete on-page metadata di sebuah situs company profile Next.js App Router. Jujur, dulu saya menganggap metadata itu pekerjaan lima menit: pasang <title> dan meta description, selesai. Kenyataannya, yang bikin audit itu lolos adalah tiga lapis metadata yang bekerja bareng: canonical dan hreflang, konfigurasi metadataBase, plus identitas JSON-LD di head.
Canonical dan hreflang: kontrak resiprokal
Helper pageMetadata() di frontend/src/components/shared/meta.ts menjadi satu sumber kebenaran untuk semua route. Ia menyusun canonical absolut ke URL produksi, alternates hreflang id/en dengan x-default mengarah ke versi Indonesia, lalu kartu Open Graph dan Twitter memakai gambar 1200x630 yang sama. Sisi Google-nya menarik: canonical paling berguna justru ketika menunjuk dirinya sendiri. Dokumentasi Search Central menyarankan menyertakan rel=canonical di halaman canonical itu sendiri, alias self-referencing canonical [2]. Tanpa sinyal ini, Google tetap akan memilih satu versi sendiri, karena tak satu pun metode kanonikalisasi itu wajib [2]. Kita cuma sedang menasihati, bukan memerintah.
Untuk hreflang, kontraknya lebih ketat. Setiap versi bahasa wajib menyebut dirinya sendiri plus semua versi lain, dan URL-nya harus fully-qualified [3]. Kalau dua halaman tidak saling menunjuk, tag-nya diabaikan begitu saja [3]. Di situs dua-bahasa, satu helper seperti ini mencegah kasus klasik: route id lengkap, route en lupa satu link, dan setengah kluster hreflang runtuh tanpa error apa pun.
metadataBase dan kebiasaan nulis URL dua kali
Di root layout, commit menyetel metadataBase: new URL(BASE_URL). Di sinilah saya menemukan hal yang bikin senyum: helper sudah menyusun URL absolut secara manual, sementara metadataBase juga dipasang. Padahal menurut dokumentasi Next.js, field metadata yang berisi URL absolut akan mengabaikan metadataBase [1]. Artinya salah satu dari dua mekanisme itu redundan. Pola yang diharapkan dokumentasi: metadataBase di root layout, field lain cukup path relatif, dan mesin yang merakit URL finalnya [1]. Dua catatan kecil yang bikin pola ini wajib dihafal: path relatif tanpa metadataBase memicu build error [1], dan title.template hanya berlaku untuk route segment anak, bukan segmen tempat ia didefinisikan [1].
JSON-LD: biar mesin tahu ini bisnis nyata
Lapis terakhir dua script JSON-LD yang diinject ke head lewat root layout. Yang pertama bertipe LocalBusiness, tipe schema.org untuk bisnis fisik atau cabang [4] yang pemakaiannya masuk kelas paling ramai: 1 juta sampai 10 juta domain menurut agregasi indeks web Google per Agustus 2026 [4]. Isinya fakta perusahaan: nama legal, alamat, telepon, tahun berdiri. Yang kedua bertipe WebSite dan disambungkan ke entitas organisasi lewat referensi @id, jadi crawler membacanya sebagai satu graf yang saling terkait, bukan dua blok lepas.
Sinyal kanonikalisasi sendiri kalau dibaca urut dari dokumentasinya: redirect jadi sinyal terkuat, rel=canonical menyusul, sitemap hanya sinyal lemah [2]. Yang bikin praktis, metode-metode ini bisa ditumpuk supaya makin efektif [2]. Commit ini memang tidak membuat redirect baru, tapi konsisten memakai satu pola URL di canonical, hreflang, sampai Open Graph, jadi semua sinyal menunjuk ke versi yang sama. Konsistensi ini juga berlaku ke dalam: link internal di situs sebaiknya selalu menunjuk ke URL canonical, bukan ke duplikatnya [2]. Tidak ada lagi kasus canonical menunjuk ke versi non-www sementara sitemap memuat versi www.
Commit ini menutup satu pola yang sering saya lewatkan di project Frontend: metadata bukan satu field, tapi tiga lapis dengan kontrak berbeda. Canonical itu sinyal preferensi yang bertumpuk, hreflang itu kluster resiprokal yang ketat, dan metadataBase itu pompa pengisi URL yang diam-diam menutup celah. Setelah ketiganya dikonsolidasi ke satu helper, nambah route baru tinggal memanggil fungsi, dan audit 16 route itu lolos tanpa drama. Praktiknya ikut jadi alat diagnosa cepat: kalau ada route baru yang kelewat tidak dipasangi helper, audit langsung menangkap canonical yang kosong, sebelum Google yang menangkapnya lewat hasil pencarian.
Sources
[1] Next.js docs: generateMetadata
[2] Google Search Central: How to specify a canonical URL
[3] Google Search Central: Localized versions of your pages
[4] schema.org: LocalBusiness