Add an uploaded vector tileset in OpenLayers
Copy JavaScript and React examples for loading an uploaded vector tileset from Mappinest TileJSON in OpenLayers.
Overview
This guide adds an uploaded vector tileset in OpenLayers 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.
This example uses mappinest.COUNTIES as the sample uploaded vector tileset. It fetches TileJSON first, then uses the returned tile template, max zoom, and source layer metadata to create the OpenLayers vector tile source.
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 OpenLayers</title>
<link href="https://unpkg.com/ol@10.9.0/ol.css" rel="stylesheet" />
<script src="https://unpkg.com/ol@10.9.0/dist/ol.js"></script>
<script src="https://unpkg.com/ol-mapbox-style@13.4.1/dist/olms.js"></script>
<style>
body { margin: 0; }
#map { height: 100vh; width: 100vw; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const apiKey = 'YOUR_MAPPINEST_KEY';
async function initMap() {
// Fetch TileJSON first, then use the returned tile template to create the OpenLayers source.
const tilejson = await fetch(
`https://api.mappinest.com/v1/tiles/mappinest.COUNTIES.json?key=${apiKey}`
).then((response) => response.json());
const tilesUrl = tilejson.tiles?.[0];
const tileMaxZoom = tilejson.maxzoom ?? 14;
if (!tilesUrl) {
throw new Error('TileJSON did not return a tile URL.');
}
const map = new ol.Map({
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([-98.5795, 39.8283]),
zoom: 3.6
})
});
// Apply the Mappinest map style before adding the uploaded tileset layer.
await window.olms.apply(
map,
`https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`
);
const layer = new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: new ol.format.MVT(),
url: tilesUrl,
maxZoom: tileMaxZoom
}),
style: new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(37, 99, 235, 0.28)' }),
stroke: new ol.style.Stroke({ color: '#1d4ed8', width: 1 })
})
});
map.addLayer(layer);
}
initMap();
</script>
</body>
</html>import { useEffect, useRef } from 'react';
import Map from 'ol/Map';
import View from 'ol/View';
import MVT from 'ol/format/MVT';
import VectorTileSource from 'ol/source/VectorTile';
import VectorTileLayer from 'ol/layer/VectorTile';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import Style from 'ol/style/Style';
import { fromLonLat } from 'ol/proj';
import { apply } from 'ol-mapbox-style';
import 'ol/ol.css';
const apiKey = 'YOUR_MAPPINEST_KEY';
export default function MappinestOpenLayersVectorTilesMap() {
const containerRef = useRef<HTMLDivElement | null>(null);
const mapRef = useRef<Map | null>(null);
useEffect(() => {
if (!containerRef.current || mapRef.current) return;
let cancelled = false;
async function initMap() {
// Fetch TileJSON first, then use the returned tile template to create the OpenLayers source.
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];
const tileMaxZoom = tilejson.maxzoom ?? 14;
if (!tilesUrl) {
throw new Error('TileJSON did not return a tile URL.');
}
const map = new Map({
target: containerRef.current,
view: new View({
center: fromLonLat([-98.5795, 39.8283]),
zoom: 3.6
})
});
mapRef.current = map;
// Apply the Mappinest map style before adding the uploaded tileset layer.
await apply(
map,
`https://api.mappinest.com/v1/styles/light/style.json?key=${apiKey}`
);
const layer = new VectorTileLayer({
source: new VectorTileSource({
format: new MVT(),
url: tilesUrl,
maxZoom: tileMaxZoom
}),
style: new Style({
fill: new Fill({ color: 'rgba(37, 99, 235, 0.28)' }),
stroke: new Stroke({ color: '#1d4ed8', width: 1 })
})
});
map.addLayer(layer);
}
void initMap();
return () => {
cancelled = true;
mapRef.current?.setTarget(undefined);
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