Library / SDK
mapbox/mapbox-gl-draw avatar
mapbox/mapbox-gl-draw

mapbox-gl-draw: giving a Mapbox GL map a pencil, and a way to add your own tool

Draw tools for mapbox-gl-js

1,092 stars610 forksJavaScriptISC

At a glance

What is it?
mapbox-gl-draw is an ISC-licensed JavaScript library that adds drawing and editing of features to a mapbox-gl-js map, with polygon, line and point modes plus a documented custom mode API. The judgement is that the mode interface is the durable part of the project, and that the CDN version pinned in the README lags the npm release.
Who is it for?
mapbox-gl-draw fits a team building a map interface where users need to sketch a boundary, trace a route or drop a point and have that geometry become structured data. It does not fit a project that is not already on mapbox-gl-js, since the library is a plugin for it, and it does not fit a team that needs a hand-drawn freehand stroke rather than a vertex-editable geometry.
Can I use it commercially?
Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A control that turns clicks into GeoJSON

mapbox-gl-draw adds support for drawing and editing features on mapbox-gl-js maps. The package description calls it a drawing component for Mapbox GL JS, and the repository is a plugin rather than a standalone mapping library.

The dependency is the first thing to note. The README states that mapbox-gl-draw requires mapbox-gl-js, and that requirement is not a suggestion. This is a component that attaches to an existing GL map, adds a control to it, and gives the user a set of tools for producing and modifying vector features.

The user's output is geometry. A polygon traced on the map becomes a feature, a line becomes a route, a point becomes a marker, and that geometry can then be read out as data. For an application where a user sketches a service area, marks an address, or draws a delivery boundary and expects that to become a record, that is the whole point of the library.

The package is `@mapbox/mapbox-gl-draw` on npm, at version 1.5.2, published as an ES module with `"type": "module"`. The `exports` map in the package manifest is explicit, with the root entry at `./index.js` and three subpath exports for the unminified bundle, the minified bundle and the stylesheet. The `main` and `browser` fields both point at `dist/mapbox-gl-draw.js`, and `style` points at the CSS.

The licence is ISC, with the `LICENSE` file at the repository root, which is a permissive grant with no copyleft obligation. That is a licensing observation rather than legal advice, and the `LICENSE` file governs.

Installing it, and the CSS step that people forget

The install is one command:

bash
npm install @mapbox/mapbox-gl-draw

Then the README drops a sentence that is easy to skim past: Draw ships with CSS, make sure you include it in your build. The controls, the buttons and the mode styling all come from that stylesheet, and a missing stylesheet produces a map with working logic and broken appearance, which is a confusing failure to debug.

With modules, the JavaScript side is two imports:

js
import mapboxgl from 'mapbox-gl';
import MapboxDraw from "@mapbox/mapbox-gl-draw";

And the CSS is imported the same way:

js
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'

There is also a CDN route, with the script and the stylesheet hosted on Mapbox's own API host under the plugins path:

html
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.js'></script>
html
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.3/mapbox-gl-draw.css' type='text/css' />

Note the version in that CDN URL: it points at v1.4.3, while the npm package is at 1.5.2. The CDN example in the README has not been kept in step with the released package, so anyone who copies it is running an older build than the one they would get from npm, and the same stale version appears in the stylesheet link.

TypeScript users get their definitions from DefinitelyTyped rather than from the package itself, with `@types/mapbox__mapbox-gl-draw` installed separately.

Adding the control and reacting to the drawn features

The example in the README is the minimum working integration, and it is worth reading because the control placement and the event handling are the two things you have to get right.

js
mapboxgl.accessToken = 'YOUR_ACCESS_TOKEN';

var map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v12',
  center: [40, -74.50],
  zoom: 9
});

var Draw = new MapboxDraw();

map.addControl(Draw, 'top-left');

map.on('load', function() {
  // ALL YOUR APPLICATION CODE
});

`Map#addControl` takes an optional second argument for the position of the control, and if no position is specified the control defaults to `top-right`. The example overrides that with `top-left`.

The `map.on('load', ...)` callback is where your application code goes, which is the ordinary mapbox-gl-js pattern: the map object exists before the style has loaded, and drawing before the style is ready is a good way to get confusing behaviour.

The full reference is in `docs/API.md`, and the README directs developers there explicitly. The live example is hosted at the Mapbox GL JS example page, which is the fastest way to see the built-in modes behaving before you wire them into your own interface.

The example uses `var` rather than `const` and `let`, and the package manifest declares `"type": "module"`, so the source is modern while the snippet is not. That is a small inconsistency in the documentation rather than a problem, but it does mean the example is not a template for modern module code.

Custom modes are the extension mechanism, and there is a process for them

The most interesting part of this project is the mode system, and the documentation is unusually explicit about how third-party work is supposed to enter the project.

For additional functionality there is a list of custom modes in `docs/MODES.md`, and the process is stated plainly. Mapbox Draw accepts functionality changes after the functionality has been proven out via a custom mode. This lets users experiment and validate their mode before entering a review process, with the stated hope of promoting innovation. When you write a custom mode, you open a pull request adding it to the list of custom modes.

So there are two routes, and the distinction matters. A custom mode is the way to add a new tool without waiting for the project to accept it, and the list in `docs/MODES.md` is a registry of modes that have been written and shared. That registry is a search index of prior art, and it is worth reading before writing your own, because someone may have solved the same problem.

The mode architecture shows up in the build scripts. There are two documentation generation steps, one for the setters and getters and one for the life cycle functions, and both pull the source of truth from specific files in `src/modes/`, namely `mode_interface_accessors.js` and `mode_interface.js`. So the mode interface is defined in code, and the documentation in `docs/MODES.md` is generated from it rather than written by hand. That is a good sign about the API's stability, because the documented surface and the implemented surface cannot drift apart silently.

The mode interface has a life cycle and a set of accessors, and the documentation sections are named to match. A custom mode implements the interface, which is what gives it the same behaviour as the built-in modes for things like entering and leaving the mode, handling clicks, and exposing the current feature state.

The build, the test runner and the release pipeline

The repository is a modern JavaScript project and the scripts tell you what it uses.

The bundler is Rollup, with `rollup.config.js` at the root, and there are separate build and minified build scripts, `rollup -c` and `rollup -c --environment MINIFY:true`, with `prepublishOnly` running both. There is a watch mode and a separate watch-bench script that uses `bench/rollup.config.js`, and a `bench/` directory for benchmarks.

The test runner is the Node built-in test runner, not Jest or Mocha. The test script is `node --test --import ./test/mock-browser.js`, and coverage is the same with `--experimental-test-coverage`. The `--import ./test/mock-browser.js` is the significant part: the library manipulates the DOM and talks to the map, so the tests run against a mock browser environment rather than a real one. The `pretest` script runs lint first, so `npm run test` lints and then tests.

The docs are generated with the `documentation` package, and the two `docs-modes-*` scripts pass `--shallow`, which extracts only the direct members of the interface objects rather than the whole tree.

The release process is spelled out, which is more than most projects bother with. You create a pull request with a version bump and a changelog update. After it is merged, on the commit merged to `main` you tag and push:

bash
git tag v{x.y.z}
git push origin v{x.y.z}

Then you create a GitHub release with the same version number and changelog after a successful run of the `npm-release.yml` workflow.

And the CDN copy is a separate step, which needs AWS credentials:

bash
git checkout v{x.y.z}
npm ci
npm run prepublishOnly
aws s3 cp --recursive --acl public-read dist s3://mapbox-gl-js/plugins/mapbox-gl-draw/v{x.y.z}

The last step is the one that explains the stale v1.4.3 in the README's CDN example. Publishing to the CDN is a manual, credentialed operation run by the maintainers, so it lags the npm release, and the documentation was not updated when it happened. That is a small operational fact with a practical consequence for anyone copying the snippet.

Maintenance state, and the naming conventions the project follows

The release history is steady. Version 1.5.0 came on 2024-12-04, 1.5.1 on 2025-11-03, and 1.5.2 on 2026-09-14. The last push to the repository was on 2026-10-13 and the repository is not archived, so the code is being worked on.

The `engines` field requires Node `^18.0.0 || >=20.0.0`, so the build tooling needs a reasonably current Node.

One detail in the README is worth noting for anyone maintaining their own fork or contributing. The project has a short section on naming actions, saying they are trying to follow standards when naming things, and linking to two sources of inspiration: Turf.js and the Shapely Python documentation. That is a deliberate choice to align naming with the geospatial ecosystem rather than with JavaScript conventions generally, and it explains why method names in the API read the way they do.

The repository also has an `ISSUE_TEMPLATE.md` and a `.github/` directory holding the `main.yml` workflow the build badge points at. There is a `cloudformation/` directory, which is for the demo and test environment rather than the library itself, and a `debug/` directory that the development workflow points the browser at.

The development loop is short, which is a good sign for a library this size:

bash
git clone [email protected]:mapbox/mapbox-gl-draw.git
npm ci
npm start & open "http://localhost:9967/debug/?access_token=<token>"

The debug page at port 9967 with an access token is the place to exercise the modes interactively, and the same `docs/MODES.md` list that serves as the registry of custom modes is where a new mode gets submitted.

Editorial conclusion

mapbox-gl-draw fits a team building a map interface where users need to sketch a boundary, trace a route or drop a point and have that geometry become structured data. It does not fit a project that is not already on mapbox-gl-js, since the library is a plugin for it, and it does not fit a team that needs a hand-drawn freehand stroke rather than a vertex-editable geometry. Verify first that the geometry types you need are covered by the built-in modes, that the custom mode API in `docs/MODES.md` gives you the hooks your own tool needs, since that is where the real extension work happens, and that the version of the library you pull matches the mapbox-gl-js version it is coupled to, because the package requires Node 18 or 20 and later for its own build.

Frequently asked questions

How do I install mapbox-gl-draw in a project?

Run `npm install @mapbox/mapbox-gl-draw`, then import the module and, importantly, the stylesheet, since the controls are styled by `dist/mapbox-gl-draw.css`. A CDN build is also published under the Mapbox API plugins path, though the version in the README's example is v1.4.3 while the npm package is at 1.5.2.

How do I add the draw control to a mapbox-gl-js map?

Create a `MapboxDraw` instance and pass it to `map.addControl(Draw, 'top-left')`. The second argument sets the control position and defaults to `top-right` when omitted.

How do I add my own drawing tool to mapbox-gl-draw?

Write a custom mode against the mode interface, then open a pull request adding it to the list of custom modes in `docs/MODES.md`. The project accepts functionality changes once they have been proven out via a custom mode, which lets you validate a mode before going through a review process.

Does mapbox-gl-draw ship TypeScript types?

The definitions come from DefinitelyTyped rather than from the package itself, and you install them separately with `npm install @types/mapbox__mapbox-gl-draw`. The full API reference is in `docs/API.md`.

What licence is mapbox-gl-draw released under?

ISC, with the `LICENSE` file at the repository root and the `license` field set to ISC in the package manifest. The package version in the manifest is 1.5.2 and it requires Node 18 or 20 and later for its own build.

Official sources

  1. License: ISC
  2. mapbox/mapbox-gl-draw on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mapbox-mapbox-gl-draw.svg)](https://hysenlabs.com/projects/mapbox-mapbox-gl-draw)

Was this useful?