Getting Started
Installation
Install LibreDraw alongside MapLibre GL JS:
npm install @sindicum/libre-draw maplibre-glBasic Usage
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import { LibreDraw } from '@sindicum/libre-draw';
// Create a MapLibre map
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [139.6917, 35.6895],
zoom: 12,
});
// Attach LibreDraw — toolbar appears automatically
const draw = new LibreDraw(map);That's it! A toolbar with draw-point, draw-line, draw, draw-rectangle, select, split, union, setback, rotate, delete, undo, and redo buttons appears on the map. Use draw-point to place points, draw-line to draw lines, draw to create polygons, or draw-rectangle to drop a rectangle with two clicks.
Note: LibreDraw does not require a separate CSS import. All styles (toolbar, map layers) are applied programmatically via JavaScript. Only
maplibre-gl.cssis needed for the base map.
Try it
With Options
const draw = new LibreDraw(map, {
toolbar: {
position: 'top-right', // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
controls: {
drawPoint: true,
drawLine: true,
drawPolygon: true,
select: true,
split: true,
union: true,
setback: true,
delete: true,
undo: true,
redo: true,
},
},
historyLimit: 50, // max undo/redo steps (default: 100)
});Localization
The toolbar and its popups are in English by default. Pass locale: 'ja' for the bundled Japanese strings, and messages to override individual strings on top of either locale:
const draw = new LibreDraw(map, {
locale: 'ja',
messages: { setbackExecute: '適用' },
});Every string is listed under Messages. An unknown locale throws a LibreDrawError.
Upgrading from 0.9.x: the style settings panel and the distance / angle inputs used to be Japanese. They are English by default since 0.10.0; add
locale: 'ja'to keep the previous labels.
Headless Mode
If you want to control everything programmatically without the toolbar:
const draw = new LibreDraw(map, { toolbar: false });
// Control modes via API
draw.setMode('draw-point');
draw.setMode('draw-line');
draw.setMode('draw-polygon');
draw.setMode('draw-rectangle');
draw.setMode('select');
draw.setMode('idle');
// Edit without any pointer input. Each call is one undo step and returns a
// structured result instead of throwing.
const rotated = draw.rotate(featureId, 90);
if (!rotated.ok) console.warn(rotated.reason); // e.g. 'not-rotatable' for a Point
draw.updateFeature(featureId, { properties: { crop: 'wheat' } });
draw.undo(); // properties back, still rotated
// The geometry operations of the split / setback / union modes are API
// calls too, so a headless page (or an AI agent) can run them.
const halves = draw.split(featureId, [
[139.7, 35.65],
[139.72, 35.67],
]);
if (halves.ok) draw.union(halves.created.map((f) => f.id)); // and back together
draw.setback(featureId, { index: 0 }, 10); // edge 0, 10 m inwardListening to Events
draw.on('create', (e) => {
console.log('Feature created:', e.feature.geometry.type, e.feature);
});
draw.on('update', (e) => {
console.log('Feature updated:', e.feature.geometry.type, e.feature);
console.log('Previous state:', e.oldFeature);
});
draw.on('delete', (e) => {
console.log('Feature deleted:', e.feature.geometry.type, e.feature);
});
draw.on('split', (e) => {
console.log(
'Polygon split:',
e.originalFeature.id,
'->',
e.features.map((f) => f.id)
);
});
draw.on('setback', (e) => {
console.log(
'Setback applied:',
e.originalFeature.id,
'edge:',
e.edgeIndex,
'distance:',
e.distance
);
});
draw.on('selectionchange', (e) => {
console.log('Selected IDs:', e.selectedIds);
});
draw.on('modechange', (e) => {
console.log(`Mode: ${e.previousMode} → ${e.mode}`);
});Working with GeoJSON
Export
// Recommended — returns a GeoJSON FeatureCollection directly
const geojson = draw.toGeoJSON();
// { type: 'FeatureCollection', features: [...] }If you need individual features as an array:
const features = draw.getFeatures();
// Returns: LibreDrawFeature[]Import
// Replace all features
draw.setFeatures({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[139.69, 35.69],
[139.7, 35.69],
[139.7, 35.68],
[139.69, 35.68],
[139.69, 35.69],
],
],
},
properties: {},
},
],
});
// Add without clearing existing
draw.addFeatures([feature1, feature2]);Cleanup
Always destroy the instance when you're done:
draw.destroy();
// After this, all methods will throw LibreDrawErrorNext Steps
- Learn about Modes (Idle, Draw Point, Draw Line, Draw, Draw Rectangle, Select, Split, Union, Setback, Rotate)
- See the full API Reference
- Try the Live Demo