Add an uploaded vector tileset in Leaflet
Copy JavaScript and React examples for loading an uploaded vector tileset from Mappinest TileJSON in Leaflet.
Overview
This guide adds an uploaded vector tileset in Leaflet through the tileset TileJSON URL.
If your tileset starts as an MBTiles or PMTiles file, upload it through the console first. See Upload Data for the full upload workflow, then return with the tileset Id and API key from API Keys & Access.
Leaflet does not render vector tiles by itself, so this workflow uses Leaflet.VectorGrid for the uploaded vector tileset. The example uses mappinest.COUNTIES as the sample dataset, fetches TileJSON first, then passes the returned tile template and source layer metadata to that plugin.
For production, replace mappinest.COUNTIES with your own tileset Id from the console. Use the source layer returned by the TileJSON vector_layers array when you add the map layer.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mappinest vector TileJSON with Leaflet</title>
<link href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" rel="stylesheet" />
<link href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
<style>
body { margin: 0; }
#map { height: 100vh; width: 100vw; }
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/@maplibre/maplibre-gl-leaflet@0.1.3/leaflet-maplibre-gl.js"></script>
<script src="https://unpkg.com/leaflet.vectorgrid@1.3.0/dist/Leaflet.VectorGrid.bundled.min.js"></script>
<script>
const apiKey = 'YOUR_MAPPINEST_KEY';
async function initMap() {
// Fetch TileJSON first, then pass the returned tile URL and layer metadata to Leaflet.VectorGrid.
const tilejson = await fetch(
`https://api.mappinest.com/v1/tiles/mappinest.COUNTIES.json?key=${apiKey}`
).then((response) => response.json());
const tilesUrl = tilejson.tiles?.[0];
// Choose the TileJSON vector_layers[].id for the layer you want Leaflet.VectorGrid to render.
const sourceLayer = tilejson.vector_layers?.[0]?.id || 'COUNTIES';
if (!tilesUrl) {
throw new Error('TileJSON did not return a tile URL.');
}
const map = L.map('map').setView([39.8283, -98.5795], 4);
L.maplibreGL({
style: `https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`,
interactive: false
}).addTo(map);
// Leaflet.VectorGrid renders vector tiles from that returned tile template.
L.vectorGrid.protobuf(tilesUrl, {
interactive: true,
vectorTileLayerStyles: {
[sourceLayer]: {
color: '#2563eb',
weight: 1,
fillColor: '#2563eb',
fillOpacity: 0.28
}
}
}).addTo(map);
}
initMap();
</script>
</body>
</html>import { useEffect, useRef } from 'react';
import L from 'leaflet';
import '@maplibre/maplibre-gl-leaflet';
import 'leaflet.vectorgrid';
import 'leaflet/dist/leaflet.css';
import 'maplibre-gl/dist/maplibre-gl.css';
const apiKey = 'YOUR_MAPPINEST_KEY';
export default function MappinestLeafletVectorTilesMap() {
const containerRef = useRef<HTMLDivElement | null>(null);
const mapRef = useRef<L.Map | null>(null);
useEffect(() => {
if (!containerRef.current || mapRef.current) return;
let cancelled = false;
async function initMap() {
// Fetch TileJSON first, then pass the returned tile URL and layer metadata to Leaflet.VectorGrid.
const tilejson = await fetch(
`https://api.mappinest.com/v1/tiles/mappinest.COUNTIES.json?key=${apiKey}`
).then((response) => response.json());
if (cancelled || !containerRef.current) return;
const tilesUrl = tilejson.tiles?.[0];
// Choose the TileJSON vector_layers[].id for the layer you want Leaflet.VectorGrid to render.
const sourceLayer = tilejson.vector_layers?.[0]?.id || 'COUNTIES';
if (!tilesUrl) {
throw new Error('TileJSON did not return a tile URL.');
}
const map = L.map(containerRef.current).setView([39.8283, -98.5795], 4);
mapRef.current = map;
(L as any).maplibreGL({
style: `https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`,
interactive: false
}).addTo(map);
// Leaflet.VectorGrid renders vector tiles from that returned tile template.
(L as any).vectorGrid.protobuf(tilesUrl, {
interactive: true,
vectorTileLayerStyles: {
[sourceLayer]: {
color: '#2563eb',
weight: 1,
fillColor: '#2563eb',
fillOpacity: 0.28
}
}
}).addTo(map);
}
void initMap();
return () => {
cancelled = true;
mapRef.current?.remove();
mapRef.current = null;
};
}, []);
return <div ref={containerRef} style={{ height: 420, width: '100%' }} />;
}URL patterns used in this example
The bold parameters are the values you replace. Prefer TileJSON as the integration request so the tile template, bounds, zoom range, and vector layer metadata stay behind one URL.
Get your free API key in API Keys & Access, then replace YOUR_KEY in the example.
Common errors
What to read next
Last updated: June 24, 2026