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
| MapLibre | OpenLayers | SDK | |
|---|---|---|---|
| Coordinates | [lng, lat] | fromLonLat([lng, lat]) | [lng, lat] |
| Rendering | WebGL (GPU, 60fps) | Canvas + WebGL | WebGL (GPU, 60fps) |
| Bundle size | ~250 KB gzipped | ~290 KB gzipped (tree-shakeable) | ~280 KB gzipped (includes MapLibre) |
| Learning curve | Moderate (source/layer model, expressions) | Steepest (projections, OGC standards, verbose API) | Easy (MapLibre API with convenience helpers) |
| 3D support | Full: terrain, fill-extrusion, globe, sky | Limited: WebGL layers, no native globe | Full (same as MapLibre) |
| Code lines | 25 | 31 | 22 |
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
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
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: '© <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
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.
Expressions are JSON arrays evaluated by the GPU: interpolate for continuous values, step for discrete ranges, match for categories.
Data-driven styling uses JavaScript style functions that return ol.style.Style objects: more flexible but runs on the CPU.
Same expression syntax and evaluation as MapLibre; all MapLibre expression types work identically in the SDK.
Which Library to Use?
Use MapLibre style expressions like interpolate and step to dynamically size and color features based on data properties.
Makes the most sense when you need the full expression language (interpolate, step, match, case) to style features declaratively without JavaScript functions.
Style vector features dynamically based on data properties using OpenLayers style functions.
A strong fit when data-driven styling must integrate with OpenLayers' style function pipeline and complex multi-layer rendering.
Style map features dynamically using data-driven expressions for color, size, and visibility in MapTiler SDK.
Brings MapLibre's expression syntax, with automatic MapTiler basemap integration.
Build Maps with AI
Generate code like these examples using mapping library skills in your IDE.