Delivery tracking, live.
mbl-whereabout adds real-time driver tracking to any Nuxt 4 app — GPS updates from the driver, a live MapLibre map for the customer, and optional road snapping via Valhalla. Requires mbl-auth for Supabase config.
Two demo pages are live — open the driver page on your phone and watch the tracking map update in real time.
Driver opens order page
Browser Geolocation API starts. GPS coordinates are posted to your server every few seconds.
Server stores location
Optionally snapped to the nearest road via Valhalla, then written to the locations table.
Customer sees it live
Supabase Realtime pushes the update. The MapLibre marker moves instantly — no polling.
Install
npm install @madebylars.com/mbl-whereabout maplibre-glConfiguration
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@madebylars.com/mbl-auth', '@madebylars.com/mbl-whereabout'],
css: ['maplibre-gl/dist/maplibre-gl.css'],
vite: {
optimizeDeps: { include: ['maplibre-gl', 'cookie'] },
},
// optional — road snapping via self-hosted Valhalla
mblWhereabout: {
valhallaUrl: 'http://localhost:8002',
},
})What you get
Live driver GPS
useDriverLocation starts the browser Geolocation API and pushes updates to your server on every position change.
Customer tracking map
useOrderTracking subscribes to Supabase Realtime. useDeliveryMap mounts a MapLibre map and moves the driver marker in real time.
Road snapping
Optional Valhalla integration snaps GPS noise to the nearest road before storing. Self-host with the included docker-compose.yml.
Built on mbl-auth
Reuses your existing Supabase config — no extra credentials. Driver routes are automatically protected by the auth middleware.
Auto-mode for drivers
Add definePageMeta({ driverTracking: true }) and the plugin starts and stops GPS automatically on mount and leave.
Minimal schema
Three tables: drivers, orders, locations. The SQL is provided — paste it into the Supabase SQL editor and you're done.
Database setup
Run this once in your Supabase SQL editor. If you enable RLS on locations, add a SELECT policy so the customer map can read rows.
-- Run in your Supabase SQL editor
create table drivers (
id uuid primary key default gen_random_uuid(),
user_id uuid,
name text,
created_at timestamptz default now()
);
create table orders (
id uuid primary key default gen_random_uuid(),
customer_id uuid,
driver_id uuid references drivers(id),
status text default 'pending',
created_at timestamptz default now()
);
create table locations (
id uuid primary key default gen_random_uuid(),
order_id uuid references orders(id) unique,
driver_id uuid references drivers(id),
lat double precision not null,
lng double precision not null,
accuracy float,
snapped_lat double precision,
snapped_lng double precision,
updated_at timestamptz default now()
);Driver page
Add the meta flag for auto-mode, or call useDriverLocation() manually for full control.
<script setup lang="ts">
// Auto-mode: add the meta flag and the plugin handles everything
definePageMeta({ driverTracking: true })
// Or control it manually:
const { start, stop, isTracking, error } = useDriverLocation()
await start('order-uuid')
</script>Customer tracking page
useOrderTracking subscribes to Supabase Realtime. useDeliveryMap mounts the MapLibre map and moves the marker on every update.
<template>
<div id="delivery-map" style="width: 100%; height: 500px;" />
</template>
<script setup lang="ts">
const route = useRoute()
const { driverLocation } = useOrderTracking(route.params.orderId as string)
useDeliveryMap('delivery-map', { driverLocation, zoom: 15 })
</script>Module options
| Option | Default | Description |
|---|---|---|
| valhallaUrl | 'http://localhost:8002' | Road-snapping endpoint. Omit to skip snapping. |
| tileUrl | OpenStreetMap | Map tile URL passed to MapLibre GL. |