Maps and a globe on free vector tiles. No API key, no bundle tax.
Maps are where ownership usually stops: a hosted provider, a key in an
environment variable, a bill that scales with your success. This package
renders on OpenFreeMap vector tiles instead, free and key-less, so a fresh
clone shows a working map and production never leaks a credential. The
components are source-shipped .tsx on top of MapLibre GL; read the code,
then keep it.
Install
bun add @voila.dev/ui maplibre-gl@import "@voila.dev/ui/styles/sources.css";Or copy the source, it's the same files.
Peers: react@19, react-dom@19, tailwindcss@4.
The 3-minute win
A city map with a marker. onReady hands you the live MapLibre instance, so
markers, sources and layers are ordinary MapLibre code.
import { MapView } from "@voila.dev/ui/map-view";
import maplibregl from "maplibre-gl";
export function VenueMap() {
return (
<MapView
className="h-80"
center={[2.3522, 48.8566]}
zoom={11}
onReady={(map) => {
new maplibregl.Marker().setLngLat([2.3522, 48.8566]).addTo(map);
}}
unavailableFallback={<p>This browser has no WebGL.</p>}
/>
);
}Mental model
MapView owns the canvas, the MapLibre lifecycle and the navigation control,
and hands you the instance; what goes on the map is yours. RadiusMap builds
on it for the common "a place and a radius" case, and GlobeView puts the
same tiles on MapLibre's globe projection with rotation and markers built in.
The basemap follows your theme: light by default, the dark variant inside a
.dark subtree, restyled live on toggle. MapLibre itself is about 270 kB
gzipped, so the rendering body sits behind React.lazy and only downloads
once a map actually mounts; until then your bundle carries none of it.
Page map
- Quick start: this page.
- MapView: the MapLibre basemap that stays out of your bundle until it mounts.
- RadiusMap: a geodesic circle around a place, viewport included.
- GlobeView: the planet, spinning, with pulsing markers.
- Basemaps: the free-tiles story, and how to swap in your own style URL.