Daftar Isi
Akhir-akhir ini gue lagi eksperimen pake Nuxt 4 buat eksplor kemampuannya dalam ngebangun aplikasi web interaktif. Salah satu hasilnya, gue coba bikin peta interaktif Jakarta yang lengkap dengan clustered markers, custom popups, dan chart terintegrasi. Tutorial ini bakal ngebahas gimana cara gue ngebangunnya pake Nuxt 4, Leaflet, Leaflet.markercluster, dan Highcharts.
Live Demo: interactive-map-nuxt.vercel.app
Source Code: GitHub Repository
Tech Stack
- Nuxt 4 – framework Vue modern dengan dukungan TypeScript yang mantap.
- Tailwind CSS – buat styling yang sat-set dan responsif.
- Leaflet – library andalan buat peta interaktif.
- Leaflet.markercluster – buat pengelompokan (clustering) marker secara otomatis.
- Highcharts – buat nampilin chart di dalam popup atau offcanvas.
Setup Project
Mulai dengan bikin project Nuxt 4 baru:
npx nuxi init interactive-map-nuxt
cd interactive-map-nuxt
npm install
Install dependencies yang dibutuhin:
npm install leaflet leaflet.markercluster highcharts @nuxtjs/tailwindcss
Konfigurasi di nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'],
css: [
'leaflet/dist/leaflet.css',
'@/assets/css/main.css'
],
devtools: { enabled: true },
})
Struktur Folder
interactive-map-nuxt/
├─ app/
│ ├─ layouts/
│ │ └─ default.vue
│ ├─ pages/
│ │ └─ index.vue
│ └─ composables/
│ └─ useJakartaMap.ts
├─ assets/css/main.css
└─ public/jakarta.json
Membuat Map
<template>
<div ref="mapContainer" class="fixed inset-0 z-0"></div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const mapContainer = ref<HTMLDivElement | null>(null);
onMounted(async () => {
if (!mapContainer.value) return;
const L = await import('leaflet');
const map = L.map(mapContainer.value, {
center: [-6.2088, 106.8456], // Jakarta
zoom: 12,
minZoom: 11,
maxZoom: 18,
scrollWheelZoom: true,
});
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '© Carto © OpenStreetMap',
subdomains: 'abcd',
maxZoom: 19,
}).addTo(map);
});
</script>
Menambahkan Marker & Clustering
import 'leaflet.markercluster';
const markers = L.markerClusterGroup();
data.features.forEach((feature) => {
const [lng, lat] = feature.geometry.coordinates;
const marker = L.marker([lat, lng]);
marker.bindPopup(`<b>${feature.properties.name}</b><br>${feature.properties.category}`);
markers.addLayer(marker);
});
map.addLayer(markers);
Custom Popup / Offcanvas

<div v-if="dialog.visible"
class="absolute bg-white shadow-lg rounded-lg p-4 w-64 transition-all duration-200"
:style="{ top: dialog.y + 'px', left: dialog.x + 'px' }">
<h3 class="font-bold text-lg">{{ dialog.data?.name }}</h3>
<p class="text-sm text-gray-600">Kategori: {{ dialog.data?.category }}</p>
<ul class="list-disc ml-4 text-sm">
<li v-for="(val, year) in dialog.data?.visitors" :key="year">
{{ year }}: {{ val.toLocaleString() }}
</li>
</ul>
<button @click="dialog.visible = false" class="mt-3 text-sm text-blue-600 hover:underline">
Tutup
</button>
</div>
Integrasi Highcharts
import Highcharts from 'highcharts';
Highcharts.chart('chart-container', {
chart: { type: 'pie', height: 200 },
title: { text: 'Breakdown Pengunjung' },
series: [{
name: 'Pengunjung',
data: [
{ name: 'Domestik', y: feature.properties.visitors['2023'] * 0.7 },
{ name: 'Internasional', y: feature.properties.visitors['2023'] * 0.3 }
]
}]
});
Custom Marker Icons
const icon = L.icon({
iconUrl: '/marker-icon.png',
iconSize: [25, 41],
iconAnchor: [12, 41]
});
L.marker([lat, lng], { icon }).addTo(map);
Deployment
npm run build
npm run preview
Lanjut deploy ke Vercel. Preview: https://interactive-map-nuxt.vercel.app/
Kesimpulan
Eksperimen ini ngajarin gue banyak hal soal Nuxt 4, Leaflet, dan cara integrasi chart ke dalam peta interaktif. Sekarang lo udah punya peta yang fungsional dengan fitur:
- Marker clustering (pengelompokan marker)
- Custom popup dengan style Tailwind
- Visualisasi Highcharts
- Layout peta yang responsif dan interaktif
Lo bisa kembangin tutorial ini buat kota lain, dataset yang beda, atau bahkan visualisasi data real-time.

