MapSnippets logoMapSnippets logo

Data-Driven Styling

Dynamically style features based on data properties using expressions or style functions Each library has its own approach: here's how they compare.

Quick Recommendations

MapLibre GL JS — Makes the most sense when you need the full expression language (interpolate, step, match, case) to style features declaratively without JavaScript functions.

OpenLayers — A strong fit when data-driven styling must integrate with OpenLayers' style function pipeline and complex multi-layer rendering.

MapTiler SDK — Brings MapLibre's expression syntax, with automatic MapTiler basemap integration.

At a Glance

MapLibreOpenLayersSDK
Coordinates[lng, lat]fromLonLat([lng, lat])[lng, lat]
RenderingWebGL (GPU, 60fps)Canvas + WebGLWebGL (GPU, 60fps)
Bundle size~250 KB gzipped~290 KB gzipped (tree-shakeable)~280 KB gzipped (includes MapLibre)
Learning curveModerate (source/layer model, expressions)Steepest (projections, OGC standards, verbose API)Easy (MapLibre API with convenience helpers)
3D supportFull: terrain, fill-extrusion, globe, skyLimited: WebGL layers, no native globeFull (same as MapLibre)
Code lines253122

Code Snippets

Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.

MapLibre GL JS25 linesShow code
arrow_forwardFull example
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
  center: [15, 50],
  zoom: 6
});
map.on('load', () => {
  const cities = { type: 'FeatureCollection', features: [
    { type: 'Feature', geometry: { type: 'Point', coordinates: [14.42, 50.08] }, properties: { name: 'Prague', pop: 1300 } },
    { type: 'Feature', geometry: { type: 'Point', coordinates: [16.61, 49.20] }, properties: { name: 'Brno', pop: 380 } },
    { type: 'Feature', geometry: { type: 'Point', coordinates: [18.26, 49.82] }, properties: { name: 'Ostrava', pop: 290 } },
    { type: 'Feature', geometry: { type: 'Point', coordinates: [15.05, 50.77] }, properties: { name: 'Liberec', pop: 105 } },
    { type: 'Feature', geometry: { type: 'Point', coordinates: [13.38, 49.74] }, properties: { name: 'Plzen', pop: 175 } }
  ] };
  map.addSource('cities', { type: 'geojson', data: cities });
  map.addLayer({ id: 'city-circles', type: 'circle', source: 'cities', paint: {
    'circle-radius': ['interpolate', ['linear'], ['get', 'pop'], 100, 6, 500, 14, 1300, 24],
    'circle-color': ['interpolate', ['linear'], ['get', 'pop'], 100, '#2196F3', 700, '#FF9800', 1300, '#F44336'],
    'circle-stroke-width': 2, 'circle-stroke-color': '#fff'
  } });
  map.addLayer({ id: 'city-labels', type: 'symbol', source: 'cities',
    layout: { 'text-field': ['get', 'name'], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.5], 'text-anchor': 'top' },
    paint: { 'text-color': '#333', 'text-halo-color': '#fff', 'text-halo-width': 1 }
  });
});
OpenLayers31 linesShow code
arrow_forwardFull example
var features = [];
for (var i = 0; i < 50; i++) {
  features.push(new ol.Feature({
    geometry: new ol.geom.Point(ol.proj.fromLonLat([2.2 + Math.random() * 0.3, 48.8 + Math.random() * 0.1])),
    category: ['restaurant', 'hotel', 'museum'][Math.floor(Math.random() * 3)],
    rating: Math.floor(Math.random() * 5) + 1
  }));
}
var colors = { restaurant: '#e63946', hotel: '#2196F3', museum: '#9C27B0' };
var layer = new ol.layer.Vector({
  source: new ol.source.Vector({ features: features }),
  style: function(feature) {
    var cat = feature.get('category');
    var r = feature.get('rating');
    return new ol.style.Style({
      image: new ol.style.Circle({
        radius: 4 + r * 2,
        fill: new ol.style.Fill({ color: colors[cat] }),
        stroke: new ol.style.Stroke({ color: '#fff', width: 1.5 })
      })
    });
  }
});
var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', attributions: '&copy; <a href="https://www.maptiler.com/copyright/">MapTiler</a>' }) }),
    layer
  ],
  view: new ol.View({ center: ol.proj.fromLonLat([2.35, 48.85]), zoom: 13 })
});
MapTiler SDK22 linesShow code
arrow_forwardFull example
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
  container: 'map',
  style: maptilersdk.MapStyle.STREETS,
  center: [-98, 39],
  zoom: 3
});
map.on('load', () => {
  map.addSource('quakes', {
    type: 'geojson',
    data: 'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson'
  });
  map.addLayer({
    id: 'quake-circles', type: 'circle', source: 'quakes',
    paint: {
      'circle-radius': ['interpolate', ['linear'], ['get', 'mag'], 1, 3, 5, 15, 8, 30],
      'circle-color': ['interpolate', ['linear'], ['get', 'mag'],
        1, '#e8d5f5', 3, '#ce93d8', 5, '#9C27B0', 7, '#4a148c'],
      'circle-opacity': 0.7
    }
  });
});

Things to Know

Implementation-specific details that matter for data-driven styling.

MapLibre GL JS

Expressions are JSON arrays evaluated by the GPU: interpolate for continuous values, step for discrete ranges, match for categories.

OpenLayers

Data-driven styling uses JavaScript style functions that return ol.style.Style objects: more flexible but runs on the CPU.

MapTiler SDK

Same expression syntax and evaluation as MapLibre; all MapLibre expression types work identically in the SDK.

Which Library to Use?

auto_awesome

Build Maps with AI

Generate code like these examples using mapping library skills in your IDE.