When you integrate a map into a web app, one architectural decision shapes everything that follows: raster tiles or vector tiles. Get it right and your map is fast, beautiful, and adaptable. Get it wrong and you're shipping a pixelated, sluggish background image that fights your design system at every turn.
This guide explains the technical difference between the two formats, walks through the real-world trade-offs that actually matter to a frontend developer, and gives you a clear decision framework. By the end, you'll know exactly which format to choose for your project and how to get started with vector tiles on MapAtlas.
The tile format question comes up early in almost every mapping project, and the internet is full of surface-level answers that stop at "vector tiles are newer." This guide goes deeper, covering rendering pipelines, payload sizes, browser compatibility, styling control, and the specific scenarios where raster tiles remain the right call.
If you're already building and just need the code, jump to the Quick Start section.
What Are Raster Tiles?
A raster tile is a pre-rendered PNG or JPEG image, typically 256×256 or 512×512 pixels, representing a fixed slice of the world at a specific zoom level. When a user pans or zooms, the browser requests the adjacent or higher-resolution tiles, which the server has already rendered and cached.
The rendering happens entirely server-side. The tile image that arrives in the browser is a finished picture, pixels are already placed, colors are already baked in. The browser's job is simply to display it.
How raster tile servers work
A raster tile stack typically looks like this:
- Raw geographic data (OpenStreetMap, cadastral data, elevation models) is processed into a database.
- A rendering engine (Mapnik is the classic choice) pre-renders tiles at every zoom level into a tile cache.
- A tile server (TileServer GL in raster mode, or a managed CDN) serves the cached images by
/{zoom}/{x}/{y}.pngURL convention.
The result is simple, fast, and stateless. Any HTTP client can request a tile, a browser, a mobile app, a server-side process generating a map image, or a GIS desktop application.
Raster tile strengths
- Zero client-side rendering overhead. The browser just displays an image. No WebGL required, no shader compilation, no font rendering.
- Universal compatibility. Works in any browser, any version, including Internet Explorer and headless rendering environments.
- Predictable appearance. The map looks exactly as the tile server rendered it. No client-side variability.
- Easy server-side image generation. Perfect for generating static map images for email, PDF reports, or social sharing cards.
Raster tile weaknesses
- Large file sizes. A 256×256 PNG tile ranges from a couple of KB over open country to around 40-45 KB over a dense city centre. A Retina (512×512) tile is four times the pixel count.
- Pixelation on high-DPI screens. A tile rendered for a 1x display looks blurry on a 2x Retina screen unless you serve @2x tiles, doubling your bandwidth.
- No rotation or tilt. If you rotate a raster map, the text labels rotate with it and become unreadable. This is why raster maps are almost always shown north-up.
- No client-side style changes. Want to change the road color, hide POI icons, or switch to dark mode? You need a new tile set, pre-rendered server-side.
- Zoom interpolation. Between integer zoom levels, the browser scales the existing image, which blurs it. The "smooth zoom" feel you get with vector tiles doesn't exist with raster.
What Are Vector Tiles?
A vector tile contains the raw geographic data for a map region, road geometries, building polygons, water bodies, land use areas, label placement data, encoded as compact binary (usually Mapbox Vector Tile format, .mvt). The tile contains no pixels. It describes what exists in that region, not how it should look.
The rendering happens entirely client-side, using WebGL. When you load a Mapbox GL JS-compatible map (like MapAtlas), the SDK requests vector tiles, receives geometry data, and uses your style specification to draw every road, building, and label to a <canvas> element in real time, at up to 60 frames per second on capable hardware.
Inside an MVT file
The Mapbox Vector Tile specification is an open Protocol Buffers encoding. Each tile holds a set of named layers, typically roads and railways, building footprints, place names, land use, water, and administrative boundaries. Every feature in a layer carries a geometry (point, line, or polygon) and attributes such as name, class, and language variants.
Geometry coordinates are integers on a tile-local grid, 4096 units per side by default, rather than full-precision latitude and longitude. That quantisation is a large part of why MVT files are so small. Tile services usually expose them at a path like /{z}/{x}/{y}.pbf or .mvt, and because the geometry of the world changes slowly, CDN edges can cache them for hours or days while the map library prefetches the tiles around the current view.
How vector tile rendering works
Vector tile (.mvt) + Style JSON → WebGL shader → Canvas pixels
The style JSON (a structured file you control) tells the renderer: draw roads in this color, draw building extrusions at this height, use this font for labels, hide these layer types below zoom 12. The style can be changed at runtime, without fetching new tiles.
Vector tile strengths
- Fewer downloads as you zoom. A vector tile is fetched up to the source's maximum zoom (often 14) and reused for every closer zoom level, while a raster map fetches four new tiles for each zoom step. In dense city centres a single vector tile can be larger than its raster equivalent, so the savings come from reuse rather than smaller files.
- Crisp at any resolution. Vector geometry scales mathematically. A Retina display gets the same tile as a standard display, the renderer simply outputs more pixels. No @2x tile variants needed.
- Smooth zoom and rotation. Because the renderer works on raw geometry, zoom can be interpolated continuously (not just at integer levels) and the map can be pitched and rotated with labels always upright.
- Full client-side style control. Change any visual property, road color, label font, layer visibility, opacity, by updating the style object. No new tile requests. Dark mode is a style JSON swap, not a new tile set.
- 3D building extrusion. Vector tiles include building height metadata. Renderers can extrude polygons in 3D using WebGL. Raster tiles have no concept of elevation.
- Data-driven styling. Color roads by speed limit, shade land use by category, scale markers by population, all driven by data properties in the tile without touching the server.
Vector tile weaknesses
- WebGL required. No WebGL support means no vector tile rendering. This excludes very old browsers (IE11) and certain headless environments.
- Client-side CPU/GPU load. The rendering happens on the user's device. On low-end Android phones or heavily loaded desktops, complex map styles with many layers can cause frame drops.
- More complex initial setup. You need a rendering library (Mapbox GL JS, MapLibre GL, or the MapAtlas SDK) and a style JSON, not just an
<img>tag. - Font and glyph loading. Label rendering requires glyph files to be served separately. This adds a few extra network requests on first load.
How Tile URLs Work: z/x/y and Zoom Levels
Raster and vector tiles share one addressing scheme, and it explains most of what you see in a map's network tab. The scheme is the Web Mercator tile pyramid, the same one used by the OpenStreetMap slippy map convention.
At zoom 0 the whole world is one square tile. Every zoom step splits each tile into four, so zoom level z has 2z columns and 2z rows. A tile URL names one square in that grid: z is the zoom level, x the column counted from the antimeridian eastwards, and y the row counted from the top (about 85.05 degrees north) downwards.
| Zoom | Tiles in the world | Tile width at the equator | Tile width in Amsterdam (52.4° N) | Metres per pixel in Amsterdam (256 px tile) |
|---|---|---|---|---|
| 0 | 1 | 40,075 km | 24,470 km | 95,584 |
| 5 | 1,024 | 1,252 km | 765 km | 2,987 |
| 10 | 1,048,576 | 39.1 km | 23.9 km | 93.3 |
| 12 | 16,777,216 | 9.8 km | 6.0 km | 23.3 |
| 14 | 268,435,456 | 2.4 km | 1.5 km | 5.8 |
| 16 | 4,294,967,296 | 611 m | 373 m | 1.5 |
| 18 | 68,719,476,736 | 153 m | 93 m | 0.4 |
The widths come from the Earth's equatorial circumference in Web Mercator (40,075,017 m) divided by 2z, times the cosine of the latitude. That cosine is why a tile covers less ground in Amsterdam than at the equator, and why Web Mercator maps enlarge northern countries.
To find the tile for a point, convert its longitude and latitude to the grid:
// Slippy map tile for a WGS84 coordinate (OpenStreetMap convention, y counted from the top).
function lngLatToTile(lng, lat, z) {
const n = 2 ** z;
const x = Math.floor(((lng + 180) / 360) * n);
const rad = (lat * Math.PI) / 180;
const y = Math.floor(((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2) * n);
return { z, x, y };
}
lngLatToTile(4.8926, 52.3731, 14); // Dam Square, Amsterdam: { z: 14, x: 8414, y: 5384 }
So https://tiles.example.com/14/8414/5384.pbf is the vector tile that holds Dam Square at zoom 14, and the raster server would name the same square 14/8414/5384.png. Two details catch people out:
- TMS flips y. The older TMS convention counts rows from the bottom, so its y is 2z - 1 - y. MBTiles files store tiles that way. If every tile loads but the map shows the southern hemisphere, the row order is flipped.
- Vector tiles stop at a maximum zoom. A source that ends at zoom 14 has no tiles at 15 or 16. The renderer overzooms: it keeps drawing the zoom 14 tile at a larger scale, which is why the table below counts one vector tile against 16 raster tiles at zoom 16.
Tiles also work without a tile server. PMTiles packs a whole tile pyramid into one file with an internal index, and the map library reads single tiles from it with HTTP range requests, so a static file host or object storage can serve a map. We compare PMTiles with GeoPackage, GeoParquet and FlatGeobuf in a separate article in February 2027.
Performance Comparison: Real Numbers
To make the trade-off concrete, here is how central Amsterdam at zoom level 14 compares on public OpenStreetMap tile servers (measured September 2026):
| Metric | Raster (PNG 256px) | Vector (.mvt) |
|---|---|---|
| Tile size | 43 KB | 326 KB (brotli), reused for zoom 15 and closer |
| Tiles to view the same area at zoom 16 | 16 | 1 (overzoomed) |
| Requests for Retina | 1 (@2x tile) | 1 (same tile) |
| Zoom interpolation | Pixel scaling (blurry) | Mathematical (crisp) |
| Rotation support | No | Yes |
| Style change | New tile request | Zero requests |
| WebGL required | No | Yes |
At scale, the difference shows up in request counts more than in bytes. A peer-reviewed load test found that zoom and pan interactions fetched one or two vector tiles against 40 raster tiles, although the vector tiles carried more data. For a medium-traffic app generating 10 million tile requests per month, fewer requests matter, because the tile request is the unit a maps API bills on. The maps API pricing page shows the request tiers for tiles alongside geocoding and routing.
When to Use Raster Tiles
Raster tiles are not obsolete. There are clear scenarios where they remain the right choice:
Legacy system integration. If you're embedding a map in a .NET WebForms app, a server-rendered Rails view, or an environment where you control only HTML and basic JavaScript, a raster tile layer via Leaflet or OpenLayers is simpler and more reliable than introducing a WebGL renderer.
Server-side map image generation. If you need to render map snapshots on a Node.js server for email templates, PDF exports, or open graph images, headless raster rendering is your path. Tools like node-canvas plus a raster tile source work where WebGL does not.
Satellite and aerial imagery. Aerial photography is raster data by nature. Even on a vector map, the satellite imagery layer is always delivered as raster tiles. Hybrid maps mix both formats, vector for roads and labels, raster for the photographic base.
Very simple use cases. Embedding a static location map on a contact page? A simple Leaflet + OpenStreetMap raster setup takes 20 minutes and has zero JavaScript complexity. The upgrade to vector tiles is not justified for a non-interactive location pin.
When to Use Vector Tiles
Choose vector tiles for any modern web application where you care about:
- Custom brand styling. If your design system has specific colors, your map should too. Vector tiles make this a JSON change.
- Dark mode support. A dark style JSON is one configuration file. Switching between light and dark is a
map.setStyle()call. - High-DPI / Retina sharpness. Especially important for mobile-first apps and any UI where crisp rendering is a design priority.
- Smooth user experience. The continuous zoom interpolation and smooth rendering of a vector map are immediately noticeable to users. Raster maps feel clunky by comparison in interactive contexts.
- Data visualization layers. Adding a heatmap, a choropleth, or a cluster layer to a vector map requires only client-side configuration. On a raster map, you'd tile-blast a new layer or use a canvas overlay hack.
- 3D or tilted perspectives. Only achievable with vector tiles and WebGL.
Quick Start: Vector Tiles with MapAtlas
MapAtlas serves vector tiles via a Mapbox GL JS-compatible API. If you've used Mapbox GL JS before, migration is a style URL change and an API key swap. If you're starting fresh, here's the complete setup.
Install the SDK
npm install @mapmetrics/mapmetrics-gl
Or via CDN for plain HTML projects:
<link rel="stylesheet" href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css" />
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
Render a vector map
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'map', // ID of your <div>
style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
center: [4.9041, 52.3676], // [longitude, latitude], Amsterdam
zoom: 12,
pitch: 0, // 0 = top-down, 60 = 3D tilted
bearing: 0, // 0 = north-up, any value = rotated
});
The style URL points to a JSON file hosted on MapAtlas servers that defines all layers, fonts, and sources. You can also host a custom style JSON yourself to control every visual aspect of the map, see the Map Visualization & Styling guide.
Switch to dark mode at runtime
const LIGHT_STYLE = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY';
const DARK_STYLE = 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_DARK_STYLE_ID/style.json&token=YOUR_API_KEY';
let isDark = false;
document.getElementById('toggle-dark').addEventListener('click', () => {
isDark = !isDark;
map.setStyle(isDark ? DARK_STYLE : LIGHT_STYLE);
});
No new tile requests are triggered for data that's already cached. Only the style changes, the tiles remain the same binary geometry, re-rendered with new colors.
Add a GeoJSON data layer
One of the most powerful vector tile features is overlaying your own data as a styled layer:
map.on('load', () => {
// Add your own data source
map.addSource('delivery-zones', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [[[4.85, 52.34], [4.95, 52.34], [4.95, 52.40], [4.85, 52.40], [4.85, 52.34]]],
},
properties: { zone: 'Zone A', capacity: 150 },
},
],
},
});
// Style it as a colored fill layer
map.addLayer({
id: 'delivery-zones-fill',
type: 'fill',
source: 'delivery-zones',
paint: {
'fill-color': '#97C70A',
'fill-opacity': 0.2,
},
});
map.addLayer({
id: 'delivery-zones-outline',
type: 'line',
source: 'delivery-zones',
paint: {
'line-color': '#97C70A',
'line-width': 2,
},
});
});
This pattern, add a source, add a layer, paint with style expressions, is the same for heatmaps, route lines, isochrone polygons, and choropleth maps. It works because vector tiles give the renderer raw geometry, not finished pixels.
Migrating from Raster to Vector
If you have an existing Leaflet + raster tile setup and want to upgrade, here's the minimal migration path:
- Replace Leaflet with the MapAtlas SDK (or MapLibre GL JS if you prefer a fully open-source renderer).
- Replace the tile URL with a MapAtlas style JSON URL.
- Move overlays from Leaflet layers to GL JS sources and layers.
- Test on WebGL-lacking environments. Add a fallback or graceful degradation message for the small percentage of users who will not have WebGL available.
The functional result is a map that looks sharper, loads faster, and can be restyled without touching the server.
For a full walkthrough of adding markers, popups, address search, and React integration on top of a vector tile base, see How to Add Interactive Maps to Your Website.
To push the styling further, dark mode, brand colors, custom fonts, watermark removal, see How to Style a Custom Branded Map.
And if you're comparing providers on cost, data residency, and features, Google Maps API alternatives for EU developers covers the options.
Summary: The Decision Framework
| Scenario | Recommended format |
|---|---|
| Modern SPA or PWA (React, Vue, Svelte) | Vector tiles |
| Custom brand colors or dark mode | Vector tiles |
| 3D buildings or map tilt/rotation | Vector tiles |
| Data visualization overlays | Vector tiles |
| Legacy browser support (IE11) | Raster tiles |
| Server-side image generation | Raster tiles |
| Satellite/aerial imagery layer | Raster tiles |
| Simple non-interactive location pin | Raster tiles |
If you're building anything interactive, vector tiles are the default choice. Raster tiles are the deliberate exception for specific compatibility or rendering constraints.
Ready to build with vector tiles? Sign up for a free MapAtlas API key, no credit card required. Your key works across tiles, geocoding, routing, and isochrone APIs from day one.
Frequently Asked Questions
Are vector tiles better than raster tiles?
For most modern web apps, yes. One vector tile download covers several zoom levels, the map renders with smooth zoom and rotation, and it can be restyled entirely on the client. Raster tiles are simpler to implement and work in environments that don't support WebGL, but they need new downloads at every zoom step, pixelate on high-DPI screens, and offer no client-side styling control.
Do I need WebGL to use vector tiles?
Yes. Vector tile renderers like Mapbox GL JS and the MapAtlas SDK use WebGL to draw map geometry on a canvas element. All modern browsers support WebGL. If you need to support very old browsers or server-side rendering environments without a canvas, raster tiles are the safer fallback.
Does MapAtlas support both vector and raster tiles?
MapAtlas primarily serves vector tiles via a Mapbox GL JS-compatible API. This means any developer already familiar with Mapbox GL JS can switch to MapAtlas by updating the style URL and API key, with only minor code changes.
What is the MVT format?
MVT is the Mapbox Vector Tile specification, the open, vendor-neutral standard for vector tiles. Each tile is a Protocol Buffers file holding named layers (roads, buildings, places, water and so on), where every feature has a geometry and a set of attributes. Coordinates are stored as integers on a tile-local grid, 4096 units wide by default, which keeps the files compact. MapLibre GL JS, Mapbox GL JS, OpenLayers and deck.gl all read MVT.

