Polygons & Areas
Render filled polygon areas with custom stroke and fill styles Each library has its own approach: here's how they compare.
Quick Recommendations
Leaflet — A strong choice for simple filled areas where L.polygon with coordinate arrays and style options produces a polygon in one call.
MapLibre GL JS — A strong fit when polygons need data-driven fill colors, opacity expressions, or must layer with other vector tile data.
OpenLayers — Best when polygons need advanced geometry operations like union, intersection, or buffering with OpenLayers' geometry tools.
MapTiler SDK — Brings MapLibre's fill layer approach, with automatic basemap configuration.
At a Glance
| Leaflet | MapLibre | OpenLayers | SDK | |
|---|---|---|---|---|
| Coordinates | [lat, lng] | [lng, lat] | fromLonLat([lng, lat]) | [lng, lat] |
| Rendering | DOM + Canvas (2D) | WebGL (GPU, 60fps) | Canvas + WebGL | WebGL (GPU, 60fps) |
| Bundle size | ~42 KB gzipped | ~250 KB gzipped | ~290 KB gzipped (tree-shakeable) | ~280 KB gzipped (includes MapLibre) |
| Learning curve | Easiest (simple API, great tutorials) | Moderate (source/layer model, expressions) | Steepest (projections, OGC standards, verbose API) | Easy (MapLibre API with convenience helpers) |
| 3D support | No native 3D (2D only) | Full: terrain, fill-extrusion, globe, sky | Limited: WebGL layers, no native globe | Full (same as MapLibre) |
| Code lines | 10 | 22 | 24 | 23 |
Code Snippets
Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.
Leaflet10 linesShow code
const map = L.map('map').setView([48.86, 2.34], 14);
L.tileLayer('https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', {
tileSize: 256,
attribution: '© <a href="https://www.maptiler.com/">MapTiler</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
const polygon = L.polygon([
[48.8650, 2.3300], [48.8650, 2.3450],
[48.8560, 2.3450], [48.8560, 2.3300]
], { color: '#FF5722', fillColor: '#FF5722', fillOpacity: 0.3, weight: 2 }).addTo(map);
polygon.bindPopup('Selected area');MapLibre GL JS22 linesShow code
const map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
center: [14.42, 50.08],
zoom: 12
});
map.on('load', () => {
map.addSource('area', {
type: 'geojson',
data: { type: 'Feature', geometry: { type: 'Polygon',
coordinates: [[[14.39, 50.10], [14.45, 50.10], [14.45, 50.06], [14.39, 50.06], [14.39, 50.10]]] } }
});
const labels = map.getStyle().layers.find(l => l.type === 'symbol' && /label/.test(l.id));
map.addLayer({
id: 'area-fill', type: 'fill', source: 'area',
paint: { 'fill-color': '#6366f1', 'fill-opacity': 0.3 }
}, labels ? labels.id : undefined);
map.addLayer({
id: 'area-outline', type: 'line', source: 'area',
paint: { 'line-color': '#6366f1', 'line-width': 2 }
});
});OpenLayers24 linesShow code
var polygon = new ol.Feature({
geometry: new ol.geom.Polygon([[
ol.proj.fromLonLat([2.33, 48.87]),
ol.proj.fromLonLat([2.37, 48.87]),
ol.proj.fromLonLat([2.37, 48.85]),
ol.proj.fromLonLat([2.33, 48.85]),
ol.proj.fromLonLat([2.33, 48.87])
]])
});
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({ features: [polygon] }),
style: new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(230, 57, 70, 0.3)' }),
stroke: new ol.style.Stroke({ color: '#e63946', width: 2 })
})
});
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>' }) }),
vectorLayer
],
view: new ol.View({ center: ol.proj.fromLonLat([2.35, 48.86]), zoom: 14 })
});MapTiler SDK23 linesShow code
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
container: 'map',
style: maptilersdk.MapStyle.STREETS,
center: [2.34, 48.86],
zoom: 13
});
map.on('load', () => {
map.addSource('area', {
type: 'geojson',
data: { type: 'Feature', geometry: { type: 'Polygon',
coordinates: [[[2.32, 48.87], [2.36, 48.87], [2.36, 48.85], [2.32, 48.85], [2.32, 48.87]]]
}}
});
map.addLayer({
id: 'area-fill', type: 'fill', source: 'area',
paint: { 'fill-color': '#9C27B0', 'fill-opacity': 0.4 }
});
map.addLayer({
id: 'area-outline', type: 'line', source: 'area',
paint: { 'line-color': '#9C27B0', 'line-width': 2 }
});
});Things to Know
Implementation-specific details that matter for polygons & areas.
L.polygon takes [lat, lng] arrays; fill and stroke are set via options (fillColor, fillOpacity, color, weight).
Polygon is a GeoJSON source + fill layer + optional line layer for the outline; fill-color and fill-opacity are paint properties.
Polygon is an ol.Feature with ol.geom.Polygon; fill and stroke are separate ol.style objects composed into an ol.style.Style.
Same GeoJSON source + fill/line layer pattern as MapLibre; SDK adds no polygon-specific convenience methods.
Which Library to Use?
Draw filled L.polygon areas on a Leaflet map with custom stroke and fill styles.
A strong choice for simple filled areas where L.polygon with coordinate arrays and style options produces a polygon in one call.
Render a filled polygon area using a GeoJSON source and a fill layer -- the MapLibre way to draw polygons on the map.
A strong fit when polygons need data-driven fill colors, opacity expressions, or must layer with other vector tile data.
Render a styled polygon area using a vector source with ol.geom.Polygon and custom fill/stroke styles.
Best when polygons need advanced geometry operations like union, intersection, or buffering with OpenLayers' geometry tools.
Draw a filled polygon on the map using a GeoJSON source and a fill layer with MapTiler SDK.
Brings MapLibre's fill layer approach, with automatic basemap configuration.
Build Maps with AI
Generate code like these examples using mapping library skills in your IDE.