Docs / Extension layers / Vector tile layer

Vector tile layer

Draws a MapLibre vector style (style.json) into raster tiles in the browser, so any map provider can show it. Maps that cannot read a vector style themselves — Google Maps, MapKit, HERE, ArcGIS, Cesium — end up looking the same as the rest.

REACT
@mapconductor/react-vectortile
RENDERING
Raster tiles (wasm)
ESM / CJS
ESM only

01 · Overview

The renderer is written in Rust, compiled to wasm, and runs in a Web Worker. The tiles it draws are served by the SDK's local tile server (a Service Worker) and reach the map as an ordinary RasterLayer. To the map it is just a raster layer, which is why it works with every provider.

It mounts two layers: the ground (fills, lines and circles) and a transparent label layer above it. When a font range arrives only the label layer is redrawn, so the map never blanks for a moment as text appears. The attribution the style's sources ask for — OpenStreetMap and so on — is attached for you.

02 · Install

terminalnpm
npm install @mapconductor/js-sdk-core @mapconductor/js-sdk-react \
            @mapconductor/react-vectortile

@mapconductor/js-vectorstyle comes along as a dependency. The package is ESM only.

Serve the tile Service Worker

Tiles are served from /tile-sw.js — the same requirement the heatmap has. Copy the file that ships in @mapconductor/js-sdk-core into your app's public directory.

package.jsonJSON
{
  "scripts": {
    "postinstall": "cp node_modules/@mapconductor/js-sdk-core/dist/tile-sw.js public/tile-sw.js"
  }
}

A Vite production build needs the worker emitted as an ES module (the dev server does not).

vite.config.tsTypeScript
import { defineConfig } from 'vite';

export default defineConfig({
  worker: { format: 'es' },
});
CAUTION
Service Workers only run in a secure context. Plain HTTP is fine on localhost, but opening the app from another device on the LAN needs HTTPS.

03 · Basic usage

Put it inside any *MapView. style takes a style.json URL, its text, or the parsed object. The example below blanks Google Maps' own basemap (GoogleMapDesign.None) and draws the vector style in its place.

VectorTileMap.tsxTypeScript · React
import { createGeoPoint, createMapCameraPosition } from '@mapconductor/js-sdk-core';
import { GoogleMapDesign, GoogleMapView2D, useGoogleMapViewState } from '@mapconductor/react-for-googlemaps';
import { VectorTileLayer } from '@mapconductor/react-vectortile';

const TOKYO = createGeoPoint({ latitude: 35.6812, longitude: 139.7671 });
const STYLE_URL = 'https://tiles.versatiles.org/assets/styles/colorful/style.json';

export function VectorTileMap() {
  const state = useGoogleMapViewState({
    apiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
    mapDesignType: GoogleMapDesign.None,
    cameraPosition: createMapCameraPosition({ position: TOKYO, zoom: 12 }),
  });

  return (
    <GoogleMapView2D state={state} style={{ height: 480 }}>
      <VectorTileLayer style={STYLE_URL} onDiagnostics={(messages) => console.warn(messages)} />
    </GoogleMapView2D>
  );
}

style is compared by content, so building a new object with the same contents on every render does not redraw anything.

04 · Changing the look

Pass rules from style adjustments as rules and the style is drawn with them applied — colours changed, layers hidden. A role you hide (buildings, labels…) is not drawn at all.

TypeScriptTypeScript · React
import { useMemo } from 'react';
import { buildStyleRules, LayerRole } from '@mapconductor/js-vectorstyle';
import { VectorTileLayer } from '@mapconductor/react-vectortile';

const rules = useMemo(
  () => buildStyleRules((r) => {
    r.role(LayerRole.building, { visible: false });
    r.role(LayerRole.water, { colorFilter: { mix: { color: '#3a7ca5', amount: 0.6 } } });
    r.role(LayerRole.label, { opacity: 0.6 });
  }),
  [],
);

<VectorTileLayer style={STYLE_URL} rules={rules} />

Changing the rules or the style redraws the vector tiles already in memory and fetches nothing: the shapes did not change, only how they are painted. Dragging a colour slider costs no network traffic. Leave rules out and the rules compiler (wasm) is never loaded.

05 · Using it as the basemap

With asBasemap, MapLibre, Mapbox and MapTiler get the style itself instead of raster tiles, and it replaces the map's design. Those three draw vector styles natively, which is faster and sharper. opacity does not apply in that case.

TypeScriptTypeScript · React
import { MapLibreMapView2D, useMapLibreViewState } from '@mapconductor/react-for-maplibre';
import { VectorTileLayer } from '@mapconductor/react-vectortile';

<MapLibreMapView2D state={state}>
  <VectorTileLayer style={STYLE_URL} rules={rules} asBasemap />
</MapLibreMapView2D>

On every other provider asBasemap changes nothing; blank the provider's own basemap yourself by switching to its None design.

06 · Props

Prop
Default
Description
style
—
A style.json URL, its text, or the object. Required.
rules
—
js-vectorstyle rules. Omitted, the style is drawn as authored.
opacity
1
Opacity of the whole layer.
visible
true
Whether it is shown.
tileSize
512
Output tile size in px. A provider's own preference wins (ArcGIS 3D asks for 256).
maxZoom
22
Highest zoom drawn.
headers
—
Headers sent with every source tile request — for tokens and API keys.
asBasemap
false
On vector-capable maps, hand the style to the map directly.
onDiagnostics
—
Why the style may not render as intended: layer types it will not draw, sources it cannot fetch.

Tiles that need a key

TypeScriptTypeScript · React
<VectorTileLayer
  style="https://tiles.example.com/styles/basic/style.json"
  headers={{ Authorization: `Bearer ${token}` }}
/>

07 · Current limits

Labels are raster too, so they rotate with the map.
Mapbox imports and some layer types are not drawn; they are reported through onDiagnostics.
MapKit and Mappls have no blank design, so the provider's own basemap is still loaded under the opaque tiles.
It must be rendered inside a *MapView.

Related pages