Live vector tiles

Vector tiles from your PostGIS, current within seconds.

Paste a connection string, pick the tables and columns, and get a TileJSON URL for MapLibre, OpenLayers, deck.gl or anything else that reads vector tiles. When a row changes, MapSocket re-renders only the tiles its old and new shapes touch, and your map hears about it over Server-Sent Events.

What you get

  • Only the changed tiles re-render

    A trigger on each table sends the row id and its old bounds with NOTIFY. Bursts are batched per tile, so a thousand-row edit re-renders each affected tile once, at every zoom.

  • Live or snapshot

    Live sources update in one to three seconds. Snapshot sources rebuild on a schedule, for read-only roles, pooled URLs or databases you can't add triggers to.

  • Your map refreshes itself

    An update_complete event arrives over SSE with the tiles and zooms that changed. In MapLibre that's one setTiles call.

  • Shape the layers

    Choose properties and zoom ranges, filter rows with SQL, add label points (point on surface, centroid or your own column) and boundary lines, and pick a tippecanoe preset: keep everything, balanced, or small tiles.

  • Keys locked to your origins

    Publishable keys only work from the origins you allow. The connection string is encrypted at rest (AES-256-GCM) and never shown again.

  • Take the tiles with you

    Download a consistent MBTiles copy while updates continue (Starter and up), or a PMTiles archive for static hosting (Pro and up). Every source can export its tilefeed config.

How it works

  1. Paste your database URL

    We check the connection, the PostGIS version and permissions, and warn if a pooler can't carry live updates.

  2. Pick tables and columns

    Every table with a geometry column is listed with its SRID, row estimate and columns.

  3. Run the trigger once

    For live sources the dashboard generates the trigger SQL with your real id and geometry columns.

  4. Paste the tile URL into your map

    TileJSON and a z/x/y template, with a publishable key.

map.js
const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      parcels: { type: "vector", url: "https://tiles.mapsocket.com/t/acme/maps/parcels.json?key=pk_live_…" },
    },
    layers: [{ id: "parcels", type: "fill", source: "parcels", "source-layer": "parcels" }],
  },
});
live-refresh.js
const tiles = "https://tiles.mapsocket.com/t/acme/maps/parcels/{z}/{x}/{y}.pbf?key=pk_live_…";
const events = new EventSource("https://tiles.mapsocket.com/t/acme/maps/parcels/events?key=pk_live_…");

events.addEventListener("update_complete", () => {
  map.getSource("parcels").setTiles([`${tiles}&_t=${Date.now()}`]);
});

On each plan

Limit SandboxStarterProEnterprise
Tile sources 1525Unlimited
Databases 1210Unlimited
Tile requests / month 50k1M10MCustom
Updates Rebuild every 24 hoursLiveLiveLive
MBTiles download NoYesYesYes
PMTiles export NoNoYesYes

Full limits and prices on the pricing page.

Good to know

  • Live updates need a direct connection or a session pooler. A transaction pooler (Supabase port 6543, PgBouncer in transaction mode) can't carry LISTEN/NOTIFY; use snapshot mode there.
  • Tiles are built ahead of time. If you need tiles generated per request from parameterised SQL functions, pg_tileserv or Martin fits better.
  • No basemap: MapSocket serves your data. Put it on top of the basemap you already use.

Read more