Drawing Tools
Interactive drawing and editing of shapes on the map Each library has its own approach: here's how they compare.
Quick Recommendations
Leaflet — Best when you need a full-featured drawing toolbar with edit and delete controls via the mature Leaflet.draw plugin.
MapLibre GL JS — Ideal when you need lightweight vertex-click polygon drawing without a plugin dependency, using GeoJSON source updates.
OpenLayers — The right choice when you need built-in drawing with multiple geometry types (point, line, polygon, circle) and snapping support via ol.interaction.Draw.
MapTiler SDK — For MapTiler SDK users, this means MapLibre-style click-to-draw polygons plus built-in MapTiler styling.
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 | 15 | 27 | 24 | 38 |
Code Snippets
Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.
Leaflet15 linesShow code
const map = L.map('map').setView([51.5074, -0.1278], 13);
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 drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawnItems },
draw: { polygon: true, polyline: true, rectangle: true, circle: true, marker: true }
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, (e) => {
drawnItems.addLayer(e.layer);
});MapLibre GL JS27 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: 13
});
const coords = [];
const geojson = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[]] } };
map.on('load', () => {
map.addSource('draw', { type: 'geojson', data: geojson });
map.addLayer({ id: 'draw-fill', type: 'fill', source: 'draw', paint: { 'fill-color': '#2196F3', 'fill-opacity': 0.2 } });
map.addLayer({ id: 'draw-line', type: 'line', source: 'draw', paint: { 'line-color': '#2196F3', 'line-width': 2 } });
map.addSource('vertices', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
map.addLayer({ id: 'draw-points', type: 'circle', source: 'vertices', paint: { 'circle-radius': 5, 'circle-color': '#fff', 'circle-stroke-width': 2, 'circle-stroke-color': '#2196F3' } });
});
map.on('click', (e) => {
coords.push([e.lngLat.lng, e.lngLat.lat]);
const ring = [...coords, coords[0]];
geojson.geometry.coordinates = [ring];
map.getSource('draw').setData(JSON.parse(JSON.stringify(geojson)));
map.getSource('vertices').setData({ type: 'FeatureCollection', features: coords.map(c => ({ type: 'Feature', geometry: { type: 'Point', coordinates: c } })) });
});
document.getElementById('finish-btn').addEventListener('click', () => {
if (coords.length < 3) return;
map.off('click');
map.getCanvas().style.cursor = '';
});OpenLayers24 linesShow code
var source = new ol.source.Vector();
var drawLayer = new ol.layer.Vector({
source: source,
style: new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(230, 57, 70, 0.2)' }),
stroke: new ol.style.Stroke({ color: '#e63946', width: 2 }),
image: new ol.style.Circle({ radius: 6, fill: new ol.style.Fill({ color: '#e63946' }) })
})
});
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>' }) }),
drawLayer
],
view: new ol.View({ center: ol.proj.fromLonLat([2.3522, 48.8566]), zoom: 13 })
});
var draw = new ol.interaction.Draw({ source: source, type: 'Polygon' });
map.addInteraction(draw);
draw.on('drawend', function(evt) {
var geom = evt.feature.getGeometry();
var area = ol.sphere.getArea(geom);
console.log('Area: ' + (area / 1e6).toFixed(2) + ' km²');
});MapTiler SDK38 linesShow code
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
container: 'map',
style: maptilersdk.MapStyle.STREETS,
center: [14.42, 50.08],
zoom: 13
});
const coords = [];
const geojson = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[]] } };
function clickHandler(e) {
coords.push([e.lngLat.lng, e.lngLat.lat]);
var ring = coords.concat([coords[0]]);
geojson.geometry.coordinates = [ring];
map.getSource('draw').setData(JSON.parse(JSON.stringify(geojson)));
map.getSource('vertices').setData({ type: 'FeatureCollection', features: coords.map(function(c) { return { type: 'Feature', geometry: { type: 'Point', coordinates: c } }; }) });
}
map.on('load', function() {
map.addSource('draw', { type: 'geojson', data: geojson });
map.addLayer({ id: 'draw-fill', type: 'fill', source: 'draw', paint: { 'fill-color': '#9C27B0', 'fill-opacity': 0.2 } });
map.addLayer({ id: 'draw-line', type: 'line', source: 'draw', paint: { 'line-color': '#9C27B0', 'line-width': 2 } });
map.addSource('vertices', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
map.addLayer({ id: 'draw-points', type: 'circle', source: 'vertices', paint: { 'circle-radius': 5, 'circle-color': '#fff', 'circle-stroke-width': 2, 'circle-stroke-color': '#9C27B0' } });
map.on('click', clickHandler);
});
document.getElementById('finish-btn').addEventListener('click', function() {
if (coords.length < 3) return;
map.off('click', clickHandler);
map.getCanvas().style.cursor = '';
});
document.getElementById('clear-btn').addEventListener('click', function() {
coords.length = 0;
geojson.geometry.coordinates = [[]];
map.getSource('draw').setData(JSON.parse(JSON.stringify(geojson)));
map.getSource('vertices').setData({ type: 'FeatureCollection', features: [] });
map.off('click', clickHandler);
map.on('click', clickHandler);
map.getCanvas().style.cursor = 'crosshair';
});Things to Know
Implementation-specific details that matter for drawing tools.
Requires the external Leaflet.draw plugin which provides toolbar UI, edit mode, and delete controls out of the box.
No built-in draw interaction: the example implements click-to-place-vertex drawing by updating a GeoJSON source manually.
ol.interaction.Draw is built-in with support for Point, LineString, Polygon, Circle, and freehand drawing modes.
Same manual vertex-click approach as MapLibre since SDK inherits MapLibre's interaction model; no built-in draw plugin.
Which Library to Use?
Add interactive drawing and editing tools to a Leaflet map using the Leaflet.draw plugin.
Best when you need a full-featured drawing toolbar with edit and delete controls via the mature Leaflet.draw plugin.
Draw polygons on the map by clicking to place vertices and updating a GeoJSON source in real time.
Ideal when you need lightweight vertex-click polygon drawing without a plugin dependency, using GeoJSON source updates.
Let users draw points, lines, and polygons on the map using ol.interaction.Draw with a vector source.
The right choice when you need built-in drawing with multiple geometry types (point, line, polygon, circle) and snapping support via ol.interaction.Draw.
Draw polygons interactively by clicking on a MapTiler SDK map, collecting vertices into a GeoJSON source.
For MapTiler SDK users, this means MapLibre-style click-to-draw polygons plus built-in MapTiler styling.
Build Maps with AI
Generate code like these examples using mapping library skills in your IDE.