Skip to content

Peta Kantor Abu-Abu: Worker MapLibre yang Tak Pernah Minta Tile

Adityo Guni Waluyo

Ganti peta statis dengan MapLibre di Next.js 16, tile-nya nggak muncul dan console diam saja. Ternyata worker-nya yang tak pernah jalan.

Ringkasan

Peta MapLibre abu-abu kosong di Next.js 16 padahal console bersih, ternyata Turbopack gagal emit file worker-nya jadi tile nggak pernah diminta. Solusi cepatnya: load MapLibre 4.7.1 dari CDN karena build itu inline worker sebagai blob URL. Pelajarannya, kalau komponen hidup setengah tanpa error, cek tab Network duluan.

Malam itu saya baru selesai merge branch fitur halaman About Us: gambar JPEG statis peta kantor diganti komponen peta interaktif di office-map.tsx. Saya jalankan next dev, buka homepage, dan hasilnya kelihatan menjanjikan. Frame peta muncul, marker kantor nongol di posisinya masing-masing.

Dua detik kemudian senyumnya hilang. Kanvas petanya abu-abu kosong. Nggak ada satu tile pun yang ke-render. Console bersih, nggak ada error, nggak ada warning. Ini layar paling nahas buat debugging: semuanya kelihatan hidup kecuali fungsinya.

Tiga tebakan, semuanya meleset

Tebakan pertama saya: tile server-nya yang lambat, atau kena blokir adblock. Saya pakai instance publik OpenFreeMap yang gratis tanpa batas jumlah view dan request [2]. Nggak ada API key di situ, dan penggunaan komersialnya diizinkan resmi. Adblock saya matikan, halaman di-refresh, hasilnya tetap abu-abu.

Tebakan kedua: urusan bundling. Saya ubah komponennya pakai dynamic import, dengan harapan chunk yang terpisah menyelesaikan masalah. Nggak ngaruh sama sekali.

Ketiganya kalah telak sama tab Network. Setelah saya buka, baru ketahuan: nggak ada request tile yang keluar. Kalau server-nya down, pasti ada request yang gagal atau timeout. Nol request artinya library petanya belum pernah minta data ke mana-mana. Masalahnya ada di komponen petanya sendiri.

Worker yang tak pernah lahir

Konteksnya penting: Next.js 16, rilis Oktober 2025, bikin Turbopack jadi bundler default untuk dev dan build [5]. Skrip saya cuma next dev polos, jadi tanpa terasa seluruh workflow pindah ke bundler baru.

Di dokumentasinya, MapLibre menjelaskan persis apa yang terjadi: Turbopack mengubah new URL('maplibre-gl/dist/maplibre-gl-worker.mjs', import.meta.url) jadi asset ber-hash tanpa meng-emit file pendampingnya, maplibre-gl-shared.mjs. Worker gagal di import pertama, dan gejalanya sesuai pengalaman saya malam itu: peta mount tapi tak pernah request satu tile pun [1].

Gejala serupa ternyata punya banyak wujud. Di issue 86495 di repo Next.js, justru dev server-nya yang mematikan worker MapLibre karena pesan HMR ping dari worker dianggap tak dikenal, dan console-nya sama bersihnya [3]. Beda mekanisme, akhirnya mirip: peta hidup setengah. Kelas masalah bundler baru plus web worker ini nyata, dan bentuknya bisa macam-macam.

Di sini saya paham bedanya bundle module dengan runtime asset. Main thread bisa ter-build sempurna sementara file worker-nya tak pernah muncul di output. Padahal MapLibre menjalankan decoding vector tile di worker, bukan di thread utama. Tanpa worker, yang bisa tampil cuma marker DOM buatan sendiri: titik glow dari token warna, animasi pulse yang hormat sama prefers-reduced-motion, popup nama kantor. Kerangkanya ada, isi rumahnya nggak pernah datang.

Sempat terpikir buat copy pola dari project lama saya. Komponennya persis sama, dan di sana jalan mulus. Ternyata project itu masih Next.js 14 dengan webpack, yang meng-bundle worker dengan benar. Kode sama, bundler beda, hasil beda. Variabelnya bukan kodenya.

Persinggahan iframe yang saya batalkan

Di tengah jalan buntu, jalan pintas menggoda: replace dengan embed Google Maps lewat iframe. Lima menit beres, tile langsung muncul.

Tapi saya batalkan. Situs referensi yang saya tiru beda pola, dan iframe Google membawa consentscreen serta muatan lain yang nggak perlu untuk sekadar nampilkan titik koordinat kantor. Marker custom dengan glow warna aksen juga mustahil di iframe. Turunin standar komponen gara-gara bug bundler rasanya kayak ngaku kalah. Sembilan belas menit kemudian MapLibre-nya balik lagi ke component tree.

Solusi malam itu, dan jalan resminya di versi 6

Solusi yang akhirnya jalan: muat MapLibre versi 4.7.1 dari CDN, pakai build non-CSP. Build itu meng-inline worker-nya sendiri sebagai blob URL, jadi urusan resolusi file worker selesai tanpa sentuh konfigurasi bundler. Pola loadernya sederhana:

function loadMapLibre() {
  if (window.maplibregl) return Promise.resolve(window.maplibregl);
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = "https://unpkg.com/[email protected]/dist/maplibre-gl.js";
    script.onload = () => resolve(window.maplibregl);
    script.onerror = () => reject(new Error("Failed to load MapLibre"));
    document.head.appendChild(script);
  });
}

Setelah promise-nya resolve, map diinisialisasi dengan style Liberty dari OpenFreeMap, fitBounds dipanggil pakai koordinat semua kantor, dan popup di-bind ke tiap marker. Tile vector akhirnya ter-render. Satu catatan: kalau project kamu menjalankan CSP ketat yang menolak blob: di worker-src, pendekatan CDN begini bakal mental, dan dokumentasi menyarankan mengarahkan worker URL ke lokasi same-origin secara eksplisit [1].

Ke depan, jalan resminya sudah berubah. Sejak versi 6.0.0 rilis Juli 2026, MapLibre GL JS jadi ESM-only: bundle UMD termasuk build CSP tak dipublish lagi, dan build ESM memuat worker lewat URL asli sehingga worker-src blob: tak dibutuhkan [6]. Saat saya cek, versi terbarunya 6.11.1 keluar 23 September 2026 [4]. Untuk pengguna Turbopack, rekomendasi resmi sekarang: copy file worker-nya plus file shared yang jadi dependensinya ke folder public/, lalu tunjuk lewat setWorkerUrl [1]. Saya belum migrasi malam itu juga. Loader CDN jalan, dan migrasi major version bukan keputusan yang sehat di tengah malam.

Satu kebiasaan yang berubah dari insiden ini: kalau ada komponen yang hidup setengah tanpa sebaris error pun, tab Network saya buka duluan sebelum Console. Error biasanya teriak. Yang begini cuma bisa ketahuan dari request yang nggak pernah keluar.

Sources: [1] https://maplibre.org/maplibre-gl-js/docs [2] https://openfreemap.org [3] https://github.com/vercel/next.js/issues/86495 [4] https://github.com/maplibre/maplibre-gl-js/releases/tag/v6.11.1 [5] https://nextjs.org/blog/next-16 [6] https://github.com/maplibre/maplibre-gl-js/releases/tag/v6.0.0

Artikel terkait