FEATURE · DRAWING

Draw any shape, straight from the keyboard.

Area (polygon), line, point, rectangle, circle, freehand, road-snapped route, and an OpenStreetMap picker. Every tool produces standard RFC 7946 GeoJSON.

The floating toolbar with every drawing tool over a dark map, the cursor on the line tool.
The floating toolbar: every drawing tool, one click away.

Try it

  1. 1Press G, click around a block, press Enter
  2. 2Press O and drag out a circle
  3. 3Press R and drag out a rectangle

Open in full playground →

Most drawing libraries stop at the basics. Element Editor keeps going.

Drawing on a map is a narrow API surface — until it isn't. Rectangles want a drag and a square constraint. Circles don't exist in standard GeoJSON, so they either become buggy polygons or get lost on round-trip. Routes need to snap to road networks, which means an external routing call. Freehand needs to simplify as it goes, or it produces 2,000-point line strings. And sometimes the shape you want already exists on OpenStreetMap — tracing it is silly when you can just click it.

Element gives each of these cases its own tool, because each has its own interaction model. All output standard GeoJSON. Rectangle, circle and route carry a _drawType foreign member so the original intent survives round-trips through any backend.

The tools

ToolKeyProducesPersonality
AreaGPolygonVersatile
LineLLineStringStraightforward
PointPPointMinimal
RectangleRPolygon (_drawType: 'rectangle')Principled
CircleOPolygon (_drawType: 'circle')Existentially complicated
FreehandFLineString or Polygon (simplified)Chaotic good
RouteCLineString (_drawType: 'route')Opinionated
PickerXImports a shape from OpenStreetMapBorrowed

Every key is remappable. Press K to open the shortcuts panel and rebind any tool.

What each tool actually does

Area — click to place vertices. Close by clicking the first point, pressing Enter or double-clicking. The output is a Polygon; edges that cross each other aren't checked. Backspace removes the last placed vertex during drawing.

Line — click vertices. Enter or double-click to finish. Backspace removes the last placed vertex.

Point — single click places a marker. Keyboard-friendly: tab to focus, P, click.

Rectangle — click-drag from corner to corner. Hold Shift while drawing to constrain to a perfect square. A click without dragging creates nothing. Editing its points later turns it into a plain polygon (see below).

Circle — click the centre, drag to set the radius (a click without dragging places a default-sized circle). Stored as a 64-sided polygon with the _drawType: 'circle' foreign member. Entering point editing shows a notice; the first point you actually change turns it into a plain polygon and drops the _drawType (one undo brings the circle back).

Freehand — click-and-drag. The path is simplified on commit using Ramer–Douglas–Peucker, so you get a clean line (or a closed shape, with Close shape on) instead of a thousand pixel-scale points.

Route — click waypoints. Element calls a routing API to snap the path to roads. Works with OpenRouteService (config.network.routing.apiKey) or your own router; without one it uses the public OSRM demo server, which is car-only and for demos only. The output is a LineString with _drawType: 'route'; your clicks are kept in properties._waypoints, but Element doesn't re-route from them. Editing the route's points turns it into a plain line.

Picker — click anywhere on the map and Element imports the nearest OpenStreetMap feature as an editable shape. Named parks, buildings, roads, lakes, land use — whatever OSM has at that coordinate. If multiple features overlap the click point, a small popup lists the candidates and you choose. Requires zoom ≥ 15. On the MapLibre engine the shapes come straight from the map's vector tiles, with no network request; elsewhere the picker falls back to the public Overpass API, which you should proxy through your own instance for production traffic.

Things that hold across every tool

  • Drag-to-draw on rectangles and circles. Click-drag to size the shape in one motion. A click without dragging places a default-sized circle, but no rectangle, so a stray click never drops one.
  • Keep drawing, or return to select. By default the tool stays armed after a shape, so you can draw several in a row; press the tool key again or Esc to leave it. Press ⌘J (or switch on Return to select after each shape in Settings) to drop back to selection after each shape.
  • Spacebar pans the map without leaving the tool. Hold Space and drag — the cursor switches to grab, the active tool's state is preserved.
  • Backspace / Delete removes the last placed point during drawing. Misclicked one corner of a polygon? Backspace, click again. No need to cancel and restart.
  • Snapping is on by default. Clicks lock to nearby vertices and edges within 15 px. Switch it off in Settings, or hold ⌘ / Ctrl to skip it for one click. See snapping.

Show it in code

import { ElementEditor } from '@element-editor/react'

export default function App() {
  return (
    <div style={{ height: 600 }}>
      <ElementEditor
        tools={['polygon', 'line', 'point', 'rectangle']}
        onFeatureAdd={(feature) => console.log('new feature', feature)}
      />
    </div>
  )
}

The tools prop restricts which tools appear in the toolbar. Omit it (or pass 'all' via config.tools) to show every tool.

What it looks like in the GeoJSON

A polygon and a circle in one FeatureCollection:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[2.35, 48.86], [2.36, 48.86], [2.36, 48.87], [2.35, 48.86]]]
      },
      "properties": {}
    },
    {
      "type": "Feature",
      "_drawType": "circle",
      "geometry": {
        "type": "Polygon",
        "coordinates": [[[2.30, 48.85], /* ...62 more coords... */]]
      },
      "properties": {}
    }
  ]
}

_drawType is a RFC 7946 foreign member — standard tools keep it, strict ones ignore it. Your data is portable.

Things to know before you ship

  • Editing a rectangle's points makes it a plain polygon. There's no right-angle constraint: the first point you move, add or delete changes only that point and drops _drawType: 'rectangle' (one undo brings the rectangle back). Moving, rotating or scaling it with the transform box keeps it a rectangle.
  • Circles and routes turn plain on the first point edit. Entering point editing shows a notice and changes nothing yet; the first committed point change removes the _drawType (and a route's _waypoints) in the same step, so one undo restores it. Moving, rotating or scaling with the transform box keeps them a circle or route.
  • Route drawing needs a network call. Without a routing API, the route tool can't work. Set an OpenRouteService key or your own router in config.network.routing; the public OSRM fallback is car-only and for demos. routing: false hides the tool.
  • Freehand simplification is destructive. The simplification runs once on commit; the raw mouse path is not retained. There's no setting for the tolerance.
  • Picker depends on OpenStreetMap. Imported shapes are OSM data, licensed ODbL — attribute OSM if you display them publicly. The public Overpass endpoint (the fallback when the map tiles have no shape) is often busy and rate-limited; host your own for anything beyond demos.

Works with

  • Vertex editing — fine-tune any shape after drawing. Drag, insert, delete.
  • Snapping — magnetic snap to nearby points and edges while drawing.
  • Keyboard shortcuts — every key is remappable. Press K to customise.

Ready to draw?

Open in playground →

Read the docs · See all features