OnlyMapJS Latest release
GitHub Docs

← Gallery / Rasters & tiles

Add vector tiles

MVTLayer turns vector tiles into real geometry styled by your own accessors: OpenStreetMap over Venice, with no basemap at all.

Open full screen ↗ View source ↗
<!DOCTYPE html>
<!--
  MVTLayer is the vector half of the tile story. Where TileLayer receives a
  picture per tile, this one receives GEOMETRY per tile — roads, coastlines,
  building footprints — and draws it with your accessors, at your colours, at
  any zoom. Reach for it when the map has to stay styleable and pickable at
  street level without you loading a country's worth of GeoJSON.

  `data` takes the same {z}/{x}/{y} template a raster tile layer takes. Nothing
  else is different: the URL is handed to deck.gl, never fetched as rows.

  The map: OpenStreetMap's own vector tiles over Venice. Every canal, quay and
  building comes out of the tiles as real geometry, drawn here in one cyan
  palette. Zoom in — new detail arrives with each level.
-->
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>OnlyMapJS — Add vector tiles</title>
  <link rel="stylesheet" href="https://unpkg.com/@nika-js/onlymap/dist/onlymapjs.css" />
  <script type="module" src="https://unpkg.com/@nika-js/onlymap/dist/onlymap.standalone.js"></script>
  <style>
    :root { color-scheme: dark; }
    html, body { margin: 0; height: 100%; font-family: system-ui, sans-serif; }
    om-map {
      display: block; height: 100vh;
      --om-widget-bg: #0a1220;
      --om-widget-fg: #e0f2fe;
      --om-widget-muted: #7c93ad;
      --om-widget-border: #1c2f45;
      --om-widget-hover-bg: #122032;
    }
  </style>
</head>
<body>

  <om-map center="[12.334, 45.4375]" zoom="13.6" basemap="none">

    <!-- MVTLayer renders the tiles itself, so unlike a raster TileLayer it
         takes the ordinary polygon, line and point accessors and applies them
         to every feature in every tile.

         Colours here are constant, which is what turns a whole city into one
         drawing. To style by feature instead, set binary="false" and read the
         feature's own tile properties in an accessor — `$layerName` tells you
         which tile layer a feature came from, `water` and `buildings` among
         them.

         min-zoom and max-zoom are the levels the SERVICE publishes, not the
         zooms you allow the user. Below the maximum, deck.gl over-zooms the
         deepest tiles rather than asking for tiles that do not exist.

         MVTLayer extrudes. When the tiles carry a height property, add
         `extruded` with get-elevation and elevation-scale and the building
         footprints stand up as solids — 3D buildings with no model files and
         no extra data. Pitch the map so the height reads.

         The path carries a schema version — `shortbread_v1` here. Read the
         URL from your own config rather than hard-code it, so a service
         change is a config edit and not a release. Every public tile service
         also wants visible attribution; OpenStreetMap's is a condition of
         use, not a courtesy. -->

    <om-layer id="osm" type="MVTLayer"
              data="https://vector.openstreetmap.org/shortbread_v1/{z}/{x}/{y}.mvt"
              label="OpenStreetMap vector tiles" color="#38bdf8"
              min-zoom="0" max-zoom="14"
              filled stroked
              get-fill-color="[13, 42, 66, 190]"
              get-line-color="[56, 189, 248]"
              get-line-width="1.4"
              line-width-units="pixels" line-width-min-pixels="0.7"
              get-point-radius="1.6" point-radius-units="pixels"
              opacity="0.95"
              pickable></om-layer>

    <om-widget type="zoom-controls" position="bottom-right"></om-widget>

    <om-widget position="top-left">
      <style>
        .panel { background: rgba(10,18,32,.9); color: #dbeafe; padding: 10px 14px;
                 border-radius: 8px; font-size: 13px; max-width: 260px; line-height: 1.45; }
        .panel b { color: #7dd3fc; }
      </style>
      <div class="panel">
        <b>Venice as vectors.</b> No basemap is loaded — every line on screen
        came out of a vector tile and is styled by the attributes on one layer.
      </div>
    </om-widget>

    <om-widget type="attribution"
               text="Vector tiles © OpenStreetMap contributors, ODbL"
               position="bottom-start"></om-widget>

    <om-fallback>
      <p style="font: 15px system-ui, sans-serif; padding: 24px; max-width: 42ch">
        <strong>This map needs JavaScript.</strong><br />
        Open this file in a web browser such as Chrome, Safari, or Firefox.
      </p>
    </om-fallback>

  </om-map>

</body>
</html>

The snippet above loads the latest release. The demo above it is pinned to v0.5.3, so it keeps working when a new version ships.