Add an uploaded vector tileset in Mapbox GL JS
Copy JavaScript and React examples for loading an uploaded vector tileset from Mappinest TileJSON in Mapbox GL JS.
Overview
This guide adds an uploaded vector tileset in Mapbox GL JS through the tileset TileJSON URL. Mapbox GL JS can use that TileJSON URL as source metadata, so it can discover the tile template, bounds, and zoom range without hardcoding an XYZ tile 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.
This example uses mappinest.COUNTIES as the sample uploaded vector tileset. It loads the TileJSON URL as a vector source so the map client can use the tile template, bounds, and zoom metadata from one endpoint.
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 Mapbox GL JS</title>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.16.0/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; }
#map { height: 100vh; width: 100vw; }
</style>
</head>
<body>
<div id="map"></div>
<script src="https://api.mapbox.com/mapbox-gl-js/v3.16.0/mapbox-gl.js"></script>
<script>
// Mapbox GL JS needs its own runtime token. Mappinest API requests use apiKey.
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const apiKey = 'YOUR_MAPPINEST_KEY';
// Use the layer id returned in TileJSON vector_layers[].id.
const SOURCE_LAYER = 'COUNTIES';
const map = new mapboxgl.Map({
container: 'map',
style: `https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`,
center: [-98.5795, 39.8283],
zoom: 3.6
});
map.addControl(new mapboxgl.NavigationControl(), 'top-right');
map.on('load', () => {
// TileJSON lets Mapbox GL JS discover the tile URL, bounds, and zoom range.
map.addSource('mappinest-counties', {
type: 'vector',
url: `https://api.mappinest.com/v1/tiles/mappinest.COUNTIES.json?key=${apiKey}`
});
map.addLayer({
id: 'counties-fill',
type: 'fill',
source: 'mappinest-counties',
'source-layer': SOURCE_LAYER,
paint: {
'fill-color': '#2563eb',
'fill-opacity': 0.28
}
});
map.addLayer({
id: 'counties-line',
type: 'line',
source: 'mappinest-counties',
'source-layer': SOURCE_LAYER,
paint: {
'line-color': '#1d4ed8',
'line-width': 1
}
});
});
</script>
</body>
</html>import { useEffect, useRef } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
// Mapbox GL JS needs its own runtime token. Mappinest API requests use apiKey.
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const apiKey = 'YOUR_MAPPINEST_KEY';
// Use the layer id returned in TileJSON vector_layers[].id.
const SOURCE_LAYER = 'COUNTIES';
export default function MappinestMapboxVectorTilesMap() {
const containerRef = useRef<HTMLDivElement | null>(null);
const mapRef = useRef<mapboxgl.Map | null>(null);
useEffect(() => {
if (!containerRef.current || mapRef.current) return;
const map = new mapboxgl.Map({
container: containerRef.current,
style: `https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`,
center: [-98.5795, 39.8283],
zoom: 3.6
});
mapRef.current = map;
map.on('load', () => {
// TileJSON lets Mapbox GL JS discover the tile URL, bounds, and zoom range.
map.addSource('mappinest-counties', {
type: 'vector',
url: `https://api.mappinest.com/v1/tiles/mappinest.COUNTIES.json?key=${apiKey}`
});
map.addLayer({
id: 'counties-fill',
type: 'fill',
source: 'mappinest-counties',
'source-layer': SOURCE_LAYER,
paint: {
'fill-color': '#2563eb',
'fill-opacity': 0.28
}
});
map.addLayer({
id: 'counties-line',
type: 'line',
source: 'mappinest-counties',
'source-layer': SOURCE_LAYER,
paint: {
'line-color': '#1d4ed8',
'line-width': 1
}
});
});
return () => {
map.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