wakeb-map
A free, multi-provider Vue 3 map library supporting Leaflet, Mapbox GL JS, Google Maps, and custom providers.
Default engine: Leaflet + OpenStreetMap — zero API key needed.
Includes a built-in SVG vector map engine (world maps, regions, markers, lines — no external dependencies).
Full offline support, 19 composables, 3 components, and 30+ utility functions.

Features
Components
| Component |
Description |
<WakebMap> |
Multi-provider map — markers, polylines, circles, polygons, drawing, fullscreen, layer switching |
<WakebMapPicker> |
Ready-made location picker — search, pin, confirm, address display |
<WakebVectorMap> |
Pure SVG vector map — world regions, data visualization, series, tooltips (zero deps) |
Composables (19)
| Category |
Composables |
| Core |
useWakebMap · useDrawing · useGeofence · useMarkerCluster · useWakebTracking |
| Fleet |
useFleetTracking · useRouteFollowing |
| Visualization |
useChoropleth · useHeatmap · useTimeSlider |
| Routing |
useRouting · useGeocoder · useElevation |
| Overlays |
useImageOverlay · useWMS · useLiveData |
| UI |
useMapScreenshot · useMinimap |
| Vector |
useVectorMap |
Key Highlights
- 100% Free — no API key, no subscription, no usage limits (Leaflet default)
- Multi-Provider — switch between Leaflet, Mapbox GL JS, or Google Maps per-component with one prop
- Works Offline — built-in canvas tile layer, inline SVG markers, Haversine distance — all work without internet
- Vector Map Engine — pure SVG renderer with regions, markers, curved/straight lines, animated dash flow, fade-in animations, data series, tooltips, zoom/pan, touch support
- 5 Map Styles — streets, satellite, topo, dark, offline (auto-mapped across providers)
- Free Geocoding — Nominatim address search + reverse geocode
- Drawing — paths, polygons, rectangles, circles, measurements, with constraint boundaries
- Fleet Tracking — real-time vehicle/entity tracking with animated movement
- Full TypeScript — every type exported
Installation
npm install wakeb-map
vue (>=3.3) and leaflet (>=1.9.4) are peer dependencies:
npm install wakeb-map leaflet
npm install -D @types/leaflet
Optional: Mapbox GL JS
npm install mapbox-gl
Then register the provider:
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import { createMapboxProvider, registerProvider } from "wakeb-map";
registerProvider(createMapboxProvider(mapboxgl));
Optional: Google Maps
npm install @googlemaps/js-api-loader
Then register the provider:
import { Loader } from "@googlemaps/js-api-loader";
import { createGoogleMapsProvider, registerProvider } from "wakeb-map";
const google = await new Loader({ apiKey: "YOUR_KEY" }).load();
registerProvider(createGoogleMapsProvider(google));
Quick Start
Basic Map
<template>
<WakebMap
:center="{ lat: 24.7136, lng: 46.6753 }"
:zoom="14"
:markers="markers"
height="500px"
@click="onMapClick"
@marker-click="onMarkerClick"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { WakebMap } from "wakeb-map";
import "wakeb-map/style.css";
import type { WakebLatLng, WakebMarker } from "wakeb-map";
const markers = ref<WakebMarker[]>([
{
id: "1",
position: { lat: 24.7136, lng: 46.6753 },
title: "Riyadh",
snippet: "Capital of Saudi Arabia",
},
]);
function onMapClick(e: { position: WakebLatLng }) {
console.log("Clicked:", e.position.lat, e.position.lng);
}
function onMarkerClick(marker: WakebMarker) {
console.log("Marker:", marker.title);
}
</script>
Location Picker
<template>
<WakebMapPicker
v-model="location"
height="500px"
confirm-label="Confirm Location"
@location-picked="onPicked"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { WakebMapPicker } from "wakeb-map";
import "wakeb-map/style.css";
import type { WakebLatLng } from "wakeb-map";
const location = ref<WakebLatLng | null>(null);
function onPicked(result: { position: WakebLatLng; address: string }) {
console.log(result.position, result.address);
}
</script>
Vector Map (SVG — No External Dependencies)
<template>
<div ref="mapEl" style="width: 100%; height: 400px" />
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useVectorMap } from "wakeb-map";
const mapEl = ref<HTMLElement | null>(null);
const vectorMap = useVectorMap({
map: "world",
regionStyle: {
initial: { fill: "#d1d5db", stroke: "#fff", strokeWidth: 0.5 },
hover: { fill: "#3b82f6", fillOpacity: 0.8 },
selected: { fill: "#10b981" },
},
markerStyle: {
initial: { fill: "#ef4444", stroke: "#fff", r: 6 },
hover: { fill: "#f59e0b", r: 8 },
},
lineStyle: {
stroke: "#3b82f6",
strokeWidth: 1.5,
strokeDasharray: "6 3 6",
animation: true,
curvature: 0.15,
},
zoomButtons: true,
showTooltip: true,
regionsSelectable: true,
});
onMounted(() => {
if (mapEl.value) vectorMap.init(mapEl.value);
vectorMap.addMarkers([
{ name: "Cairo", coords: [30.04, 31.24] },
{ name: "London", coords: [51.51, -0.13] },
{ name: "Tokyo", coords: [35.68, 139.69] },
]);
vectorMap.addLines([
{ from: "Cairo", to: "London" },
{ from: "London", to: "Tokyo" },
]);
});
</script>
Plugin Setup (Optional)
Register globally to set default center, zoom, map type, provider, and offline options:
import { createApp } from "vue";
import { WakebMapPlugin } from "wakeb-map";
import "wakeb-map/style.css";
const app = createApp(App);
app.use(WakebMapPlugin, {
language: "ar",
defaultCenter: { lat: 24.7136, lng: 46.6753 },
defaultZoom: 14,
defaultMapType: "streets",
// provider: "mapbox", // uncomment to use Mapbox as default
// mapboxAccessToken: "pk.xxx", // required when using Mapbox
// provider: "google", // uncomment to use Google Maps as default
});
app.mount("#app");
Offline Mode
Full offline support — the map, markers, shapes, GPS, and distance all work without internet.
// Option 1: Plugin config
app.use(WakebMapPlugin, {
offline: true,
offlineGridColor: "#ddd",
offlineBgColor: "#f0f0f0",
});
// Option 2: Component prop
// <WakebMap map-type="offline" />
// Option 3: Manual tile layer
import { createOfflineTileLayer } from "wakeb-map";
const offlineLayer = createOfflineTileLayer({ offlineGridColor: "#ccc" });
| Feature |
Offline? |
| Map rendering |
✅ Canvas coordinate grid |
| Markers |
✅ Inline SVG icons (no CDN) |
| Shapes |
✅ Lines, circles, polygons |
| GPS |
✅ Browser hardware |
| Distance |
✅ Haversine formula (local) |
| Vector Map |
✅ Pure SVG (fully embedded) |
| Address search |
❌ Requires internet |
| Geocoding |
❌ Returns null gracefully |
Map Providers
Switching Providers
<!-- Default: Leaflet (no setup needed) -->
<WakebMap :center="center" :zoom="14" height="400px" />
<!-- Mapbox GL JS -->
<WakebMap
provider="mapbox"
access-token="pk.xxx"
:center="center"
:zoom="14"
height="400px"
/>
<!-- Google Maps -->
<WakebMap provider="google" :center="center" :zoom="14" height="400px" />
<!-- All three on the same page -->
<WakebMap :center="center" :zoom="14" height="400px" />
<WakebMap
provider="mapbox"
access-token="pk.xxx"
:center="center"
:zoom="14"
height="400px"
/>
<WakebMap provider="google" :center="center" :zoom="14" height="400px" />
Provider Comparison
| Feature |
Leaflet |
Mapbox GL JS |
Google Maps |
| API Key |
Not needed |
Required (free tier) |
Required (free tier) |
| Tile Rendering |
Raster |
Vector (WebGL) |
Vector |
| 3D Terrain |
No |
Yes |
Limited |
| Offline Support |
Yes (canvas tiles) |
No |
No |
| Bundle Size |
~40 KB |
~200 KB |
Loaded via script |
Composable Compatibility
| Composable |
Leaflet |
Mapbox |
Google |
Notes |
useWakebMap |
✅ |
✅ |
✅ |
Provider-independent (reactive) |
useWakebTracking |
✅ |
✅ |
✅ |
Provider-independent |
useFleetTracking |
✅ |
✅ |
✅ |
Provider-independent |
useRouteFollowing |
✅ |
✅ |
✅ |
Provider-independent |
useGeofence |
✅ |
✅ |
✅ |
Provider-independent |
useMarkerCluster |
✅ |
✅ |
✅ |
Provider-independent |
useTimeSlider |
✅ |
✅ |
✅ |
Provider-independent |
useLiveData |
✅ |
✅ |
✅ |
Provider-independent |
useVectorMap |
✅ |
✅ |
✅ |
Pure SVG, no map engine dependency |
useDrawing |
✅ |
❌ |
❌ |
Leaflet L.Draw only |
useChoropleth |
✅ |
❌ |
❌ |
Leaflet GeoJSON layers |
useHeatmap |
✅ |
❌ |
❌ |
Leaflet heatmap plugin |
useElevation |
✅ |
❌ |
❌ |
Leaflet elevation control |
useGeocoder |
✅ |
❌ |
❌ |
Leaflet geocoder control |
useImageOverlay |
✅ |
❌ |
❌ |
Leaflet image overlay |
useMapScreenshot |
✅ |
❌ |
❌ |
Leaflet canvas export |
useMinimap |
✅ |
❌ |
❌ |
Leaflet minimap control |
useRouting |
✅ |
❌ |
❌ |
Leaflet routing machine |
useWMS |
✅ |
❌ |
❌ |
Leaflet WMS layer |
Map Styles
| Style |
Source |
Online? |
streets |
OpenStreetMap |
Yes |
satellite |
Esri World Imagery |
Yes |
topo |
OpenTopoMap |
Yes |
dark |
CARTO Dark |
Yes |
offline |
Built-in canvas |
No |
<WakebMap map-type="satellite" />
<WakebMap map-type="dark" />
<WakebMap map-type="topo" />
<WakebMap map-type="offline" />
Composable Examples
useWakebMap
import { useWakebMap } from "wakeb-map";
const {
position,
address,
zoom,
markers,
loading,
setPosition,
pickMyLocation,
searchAddress,
addMarker,
distanceTo,
} = useWakebMap({ lat: 24.7136, lng: 46.6753 });
useDrawing
import { useDrawing } from "wakeb-map";
const drawing = useDrawing();
drawing.bindMap(leafletMap);
drawing.startDrawing("polygon", { color: "#2196F3", showMeasurements: true });
const result = drawing.finishDrawing();
useGeocoder
import { useGeocoder } from "wakeb-map";
const { search, reverse, results, loading } = useGeocoder();
await search("Kingdom Tower, Riyadh");
await reverse({ lat: 24.7136, lng: 46.6753 });
useRouting
import { useRouting } from "wakeb-map";
const { calculateRoute, route, instructions } = useRouting();
await calculateRoute(
{ lat: 24.7136, lng: 46.6753 },
{ lat: 21.4225, lng: 39.8262 },
{ profile: "driving" },
);
useHeatmap
import { useHeatmap } from "wakeb-map";
const { addPoints, setOptions } = useHeatmap(leafletMap);
addPoints([
{ lat: 24.71, lng: 46.67, intensity: 0.8 },
{ lat: 24.72, lng: 46.68, intensity: 0.5 },
]);
useFleetTracking
import { useFleetTracking } from "wakeb-map";
const fleet = useFleetTracking(leafletMap, {
showTrails: true,
animateMovement: true,
});
fleet.updateEntity({
id: "truck-1",
type: "truck",
position: { lat: 24.7, lng: 46.6 },
});
Utilities
Geocoding (Free — Nominatim)
import { reverseGeocode, geocodeAddress } from "wakeb-map";
const addr = await reverseGeocode({ lat: 24.7136, lng: 46.6753 });
const pos = await geocodeAddress("Kingdom Tower, Riyadh");
Distance & Geometry
import {
calculateDistance,
calculateBearing,
midpoint,
isPointInPolygon,
isPointInCircle,
polygonArea,
pathLength,
getBoundingBox,
convexHull,
simplifyPath,
bufferPoint,
distanceMatrix,
} from "wakeb-map";
const meters = calculateDistance(
{ lat: 24.7136, lng: 46.6753 },
{ lat: 21.4225, lng: 39.8262 },
);
GeoJSON Import/Export
import { exportToGeoJSON, importFromGeoJSON, downloadGeoJSON } from "wakeb-map";
const geojson = exportToGeoJSON({ markers, polylines, polygons, circles });
const data = importFromGeoJSON(geojsonObject);
downloadGeoJSON(geojson, "my-map.geojson");
Props Reference
<WakebMap>
| Prop |
Type |
Default |
Description |
center |
WakebLatLng |
{ lat: 24.7136, lng: 46.6753 } |
Map center |
zoom |
number |
14 |
Zoom level |
mapType |
WakebMapType |
'streets' |
Tile layer preset |
provider |
string |
'leaflet' |
Map provider ('leaflet'/'mapbox'/'google') |
accessToken |
string |
— |
Mapbox access token |
mapStyle |
string |
— |
Mapbox style URL |
markers |
WakebMarker[] |
[] |
Marker array |
polylines |
WakebPolyline[] |
[] |
Polyline array |
circles |
WakebCircle[] |
[] |
Circle array |
polygons |
WakebPolygon[] |
[] |
Polygon array |
routes |
WakebRoute[] |
[] |
Route array |
width |
string |
'100%' |
Container width |
height |
string |
'400px' |
Container height |
clickToAddMarker |
boolean |
false |
Click to place markers |
markerColor |
string |
'red' |
Default marker color |
markerDraggable |
boolean |
true |
Click-to-add markers draggable |
animateMarkers |
boolean |
false |
Smooth animated marker movement |
showScale |
boolean |
false |
Scale control |
showFullscreen |
boolean |
false |
Fullscreen button |
showLayerSwitcher |
boolean |
false |
Layer switcher overlay |
showGeolocation |
boolean |
false |
"My Location" button |
showCoordinates |
boolean |
false |
Cursor coordinate display |
showDrawingToolbar |
boolean |
false |
Drawing toolbar |
cooperativeGestures |
boolean |
false |
Ctrl+scroll to zoom |
keyboardNavigation |
boolean |
false |
Arrow key panning |
hashRouting |
boolean |
false |
Sync state with URL hash |
zoomControl |
boolean |
true |
Zoom buttons |
options |
object |
{} |
Extra map options (provider-specific) |
<WakebMapPicker>
| Prop |
Type |
Default |
Description |
v-model |
WakebLatLng |
— |
Selected location |
mapType |
WakebMapType |
'streets' |
Map style |
showSearch |
boolean |
true |
Search input |
showMyLocation |
boolean |
true |
GPS button |
showAddress |
boolean |
true |
Address display |
useCenterPin |
boolean |
true |
Center pin mode |
showConfirmButton |
boolean |
true |
Confirm button |
confirmLabel |
string |
'Confirm Location' |
Button text |
Events
<WakebMap>
| Event |
Payload |
Description |
click |
{ position, originalEvent } |
Map click |
markerClick |
WakebMarker |
Marker click |
markerDragEnd |
(marker, newPosition) |
Marker drag end |
markerDragStart |
WakebMarker |
Marker drag start |
markerAdded |
WakebMarker |
Marker added (click-to-add) |
markerRemoved |
markerId |
Marker removed |
cameraChange |
WakebCameraPosition |
Viewport change |
mapReady |
native map instance |
Map initialized |
providerReady |
WakebMapProviderInstance |
Provider instance ready |
drawComplete |
WakebDrawnPath | WakebDrawnShape | WakebMeasurement |
Drawing finished |
drawUpdate |
same |
Drawing updated |
geolocation |
WakebLatLng |
User location acquired |
update:center |
WakebLatLng |
Center changed |
update:zoom |
number |
Zoom changed |
<WakebMapPicker>
| Event |
Payload |
Description |
update:modelValue |
WakebLatLng |
Location updated |
locationPicked |
{ position, address } |
Location confirmed |
Documentation
Full interactive docs with live demos:
visit the hosted docs at wakeb-map.surge.sh
License
MIT © Kerolos Zakaria