When to Use Vector Tiles Over Raster for Web Maps

Use vector tiles when your stack requires client-side styling, feature-level interactivity, or decoupled style deployment — and your source data is structured geometry rather than continuous-tone imagery.

Vector tiles vs raster tiles decision flow A decision diagram showing four key conditions. If source data is structured geometry, interactivity or dynamic styling is needed, or CDN-efficient caching matters: choose vector tiles. If source data is continuous imagery, clients are constrained, or cartography is pixel-perfect fixed-scale: choose raster tiles. Source data type? GeoJSON / GeoParquet vs raster imagery Structured geometry (roads, parcels, POI, admin) Continuous imagery (satellite, aerial, elevation) Interactivity or dynamic style? hover, filter, theme-switch, labels Use vector tiles (.pbf) Use raster tiles (.png/.webp) also: constrained GPU/CPU, fixed-scale regulatory maps

When to Use Vector Tiles: Concrete Conditions

The following conditions are sufficient individually to justify vector tiles. Meeting two or more makes raster impractical for new infrastructure.

Condition Why vector wins Raster alternative cost
Client-side feature interactivity Geometry preserved in browser; queryRenderedFeatures() works at any zoom WMS GetFeatureInfo round-trip per click; 80–400 ms latency
Multiple themes / dark mode Style JSON swap; zero tile regeneration Separate raster tileset per theme; 2–10× storage
Style updates faster than data changes Deploy new style.json; existing .pbf cache stays valid Full raster tileset rebuild on every symbol or color change
CDN-cached geometry for multiple views One .pbf set powers all style variants N raster sets, each independently invalidated
Python-driven automated generation pipeline Idempotent Tippecanoe run; content-addressed .pbf hashes Server-side renderer must re-bake every zoom/style combination
Multilingual labels Language switched at runtime from feature attribute Pre-baked into pixels; separate raster tileset per locale

Raster tiles remain correct for: aerial/satellite imagery, hillshade terrain composites, pixel-level blending (multi-band rasters), and environments where WebGL is unavailable or GPU/CPU is severely constrained.

Specification Detail

The Mapbox Vector Tile (MVT) specification defines the binary format that .pbf tile files use. Key parameters that determine whether vector tiles are viable for a given dataset:

Parameter Value / range Notes
Tile coordinate space 4096 × 4096 units per tile Sub-pixel precision at most zoom levels
Max recommended tile size 500 KB uncompressed Larger triggers rendering stalls in MapLibre
Geometry types Point, LineString, Polygon No raster or coverage data types
Attribute value types Int, float, string, bool No binary blobs; filter predicates use these types
Protobuf compression Gzip or Brotli at the HTTP layer Not encoded into the .pbf itself
Zoom range supported z0–z22 Tippecanoe defaults to z0–z14 unless overridden

Tiles are stored in either an MBTiles SQLite container for local tooling or a PMTiles archive for CDN range-request delivery. The PMTiles format uses HTTP range requests to serve individual tiles without a tile server process, which eliminates origin infrastructure for static datasets.

Production Command

The following Tippecanoe invocation generates a production .mbtiles tileset from a GeoJSON source, with attributes pruned to the minimum needed for styling and interactivity. It demonstrates the flags that directly control the vector-vs-raster decision axes: tile size, attribute retention, and zoom range.

bash
tippecanoe \
  --output=output/roads.mbtiles \
  --maximum-zoom=14 \
  --minimum-zoom=5 \
  --drop-densest-as-needed \
  --extend-zooms-if-still-dropping \
  --include=name \
  --include=highway \
  --include=oneway \
  --simplification=4 \
  --force \
  data/roads.geojson

Flag annotations:

  • --drop-densest-as-needed — drops features at lower zooms to keep tiles under 500 KB, preserving the vector payload budget
  • --extend-zooms-if-still-dropping — adds zoom levels automatically if the dataset is too dense to fit at --maximum-zoom
  • --include=name,highway,oneway — drops every attribute not needed by the style, a direct application of dropping unused attributes to reduce tile size
  • --simplification=4 — controls geometry simplification aggressiveness; higher values reduce vertex count at the cost of shape fidelity

For a GeoParquet source (common in large-scale Python ETL), pipe through GDAL or the converting-large-geoparquet-files workflow before feeding Tippecanoe:

bash
ogr2ogr \
  -f GeoJSON /vsistdout/ \
  data/parcels.geoparquet \
  -t_srs EPSG:4326 | \
tippecanoe \
  --output=output/parcels.mbtiles \
  --maximum-zoom=16 \
  --drop-smallest-as-needed \
  --include=parcel_id \
  --include=land_use \
  --force \
  /dev/stdin

Input must be EPSG:4326 (geographic WGS 84). Tippecanoe reprojects internally to the tile coordinate space (EPSG:3857 web Mercator) during encoding.

Interaction Effects

Vector tile viability depends on three flags working together. Changing one without considering the others causes tile size or rendering problems.

--maximum-zoom and tile payload size

Higher --maximum-zoom values increase vertex density per tile because generalization is finer. At z16+ for dense urban parcel data, uncompressed tile sizes routinely exceed 500 KB without attribute pruning. Pair --maximum-zoom=16 with aggressive --include allowlisting and --simplification=8 to stay within budget. See calculating optimal max zoom for urban datasets for the tile-size-vs-zoom trade-off in detail.

--simplification and geometry fidelity

The Visvalingam vs Douglas-Peucker comparison matters here: Tippecanoe uses a variant of Visvalingam-Whyatt by default. At --simplification=4 (the recommended starting point for roads), vertex count drops 30–60% at z10 without visible artifacts at typical viewport zoom levels. Increasing to --simplification=10 reduces tile size a further 15–25% but produces noticeably jagged polygons when users zoom in.

Attribute filtering and queryRenderedFeatures()

MapLibre’s queryRenderedFeatures() only returns attributes present in the tile. If an attribute is dropped by --include allowlisting, it is invisible to click handlers and runtime filter expressions, even if it exists in the source. Define your style’s filter and popup attribute list before finalizing the --include set.

Performance Impact

Typical measurements across a 500 MB GeoJSON roads dataset (OpenStreetMap extract, Western Europe):

Metric Vector tiles (z0–z14) Raster tiles (z0–z14, 256 px PNG)
Total tileset size 1.2 GB (MBTiles) 18–40 GB (depending on symbology)
Generation time 8 min (Tippecanoe, 8 cores) 2–6 hours (server-side renderer)
Per-tile transfer (z12, urban) 35–80 KB .pbf + gzip 60–130 KB PNG
Style update cost 0 (new style.json deploy) Full raster rebuild
Client render time (z12, desktop) 8–20 ms (WebGL, MapLibre) 2–5 ms (image decode + draw)
Client render time (z12, low-end mobile) 40–120 ms 5–15 ms

The client render overhead on low-end mobile is the primary reason to stay raster when targeting broad consumer audiences without device profiling. A 120 ms frame budget hit at z12 causes visible jank during pan gestures.

CDN cache efficiency is strongly in vector’s favor: one .pbf tileset serves every style variant. Raster caches multiply linearly with the number of distinct themes or locales.

Common Mistakes

Sending un-pruned attributes in vector tiles

Symptom: tiles consistently exceed 500 KB at z14+; MapLibre logs Tile size limit exceeded warnings in the browser console.

bash
# Diagnose: inspect attribute payload fraction
tippecanoe-decode output/parcels.mbtiles 14 8192 5461 | \
  python3 -c "
import sys, json
tile = json.load(sys.stdin)
for layer in tile.get('features', []):
    print(layer.get('properties', {}).keys())
" | sort | uniq -c | sort -rn | head -20

Fix: add --include flags to Tippecanoe to allowlist only the attributes your style and click handlers need.

Passing EPSG:3857 input to Tippecanoe

Symptom: tiles render with severe coordinate offset or land in the ocean; no Tippecanoe error is raised.

bash
# Check CRS before ingesting
ogrinfo -al -so data/roads.geojson | grep -i "srs\|crs\|projection"
# Reproject if needed
ogr2ogr -f GeoJSON -t_srs EPSG:4326 data/roads_wgs84.geojson data/roads.geojson

Tippecanoe requires EPSG:4326 (longitude/latitude) input. It handles the tile projection internally.

Choosing vector tiles for a dataset that is effectively imagery

Attempting to vectorize dense 1m-resolution contour lines, hillshade polygons, or land-cover raster classes produces millions of tiny polygons that inflate tile sizes beyond any budget. The symptom is a Tippecanoe run that produces 10–100× the expected tile count with tiles consistently at the 500 KB limit even after maximum simplification.

bash
# Check feature density before committing to vector
ogrinfo -al -so data/contours.geojson | grep "Feature Count"
# If > 5M features for a country-scale dataset, raster is likely correct

Up: Vector vs Raster Tile Tradeoffs