Leaflet.Locate: the geolocation button with a long options table
A leaflet control to geolocate the user.
At a glance
- What is it?
- Leaflet.Locate is a map control that turns the browser geolocation API into a button, and its real substance is the option table: view-following modes, a compass heading with an iOS accuracy gate, an accuracy circle and a click behaviour. The catch is that the README points at a changelog with breaking changes.
- Who is it for?
- Adopt Leaflet.Locate when your map needs a locate button whose behaviour you want to control precisely, since the options table covers the awkward cases such as untilPanOrZoom, keeping the zoom level inside a range and an iOS compass accuracy threshold that most wrappers skip. Do not adopt it for a one-line geolocation call on a page without a map, and read the changelog before you pin a version, because the README asks you to check it for breaking changes.
- Can I use it commercially?
- Yes. MIT 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 7 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 November 7, 2026, and from our analysis. They are not legal advice.
Star history
- domoritz/leaflet-locatecontrol876 ★ · +1
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| domoritz/leaflet-locatecontrol | 875 | 876 |
1 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
A locate button instead of a geolocation call in your own code
Leaflet.Locate is a control that geolocates the user, and the package description calls it a useful control to geolocate the user with many options. It is listed on the official Leaflet plugins page and has a Mapbox.js plugin example, and the package description notes that it is used by osm.org and mapbox among many others.
The problem it takes off your plate is small but annoying. The browser geolocation API gives you coordinates, and turning that into a usable map control means handling permission denial, an accuracy circle, a marker, deciding what happens when the user pans away, and updating the position as it changes. The control encapsulates all of that behind a button.
The audience is front-end developers building maps with Leaflet or Mapbox.js. The README states it was tested with Leaflet 1.9.x and 2.x, and with Mapbox.js 3.3.1, in Firefox, Chrome and Safari. The repository is JavaScript, the licence is MIT, and the default branch is gh-pages, which is where the demo page and the source live together.
One thing the README does not cover is the browser permission prompt itself, so how the control behaves when a user refuses geolocation is left to the reader.
Three ways to get the files, one of them from npm
The setup is three steps: get the JavaScript and CSS files, include them in your project, and initialize the plugin.
For production the recommended route is npm:
npm install leaflet.locatecontrolThe README also offers the JsDelivr CDN as an alternative, and a download of the built files from the repository archive. The CDN example in the README uses a version placeholder, with a note to replace [VERSION] with the latest release number or remove @[VERSION] to always use the latest version:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol@[VERSION]/dist/L.Control.Locate.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol@[VERSION]/dist/L.Control.Locate.min.js" charset="utf-8"></script>For development the README suggests a clone of the repository, and states where things live:
git clone https://github.com/domoritz/leaflet-locatecontrolThe source files are in src/ and the built distribution files are in dist/. That split matters because the published npm package ships only dist, the README.md and the LICENSE, so anything you want to inspect in the source form has to come from a clone or from the CDN path into dist.
A practical note on that placeholder: pulling the latest version from a CDN at page load means a new release can change behaviour under you, and the README itself points to a changelog entry about breaking changes. Pin the version if your map is in production.
The global factory and the ESM class are two entry points
Adding the control is one line in the classic script setup:
L.control.locate().addTo(map);With npm or a bundler the README shows a different set of imports, where the plugin registers itself on the Leaflet namespace:
import "leaflet.locatecontrol"; // Import plugin
import "leaflet.locatecontrol/dist/L.Control.Locate.min.css"; // Import styles
import L from "leaflet"; // Import L from leaflet to start using the pluginAnd if you are using a bundler or ES modules, the named export is the class itself:
import { LocateControl } from "leaflet.locatecontrol";
import "leaflet.locatecontrol/dist/L.Control.Locate.min.css";In that case the README says to use new LocateControl() instead of L.control.locate(). Two ways to do the same thing, and the difference matters for tree shaking and for tests, because the side effects list in package.json marks the CSS files and the minified and UMD builds as side effects, so a bundler will not drop them from your output.
The package.json exports map confirms the shape of the distribution: the root export carries the type definitions and points at the ESM build for import and the minified build for require, with a wildcard for the dist directory so the stylesheet can be imported by path. The version in the manifest is 0.90.1 and the package is declared as an ES module.
The options table is where the actual behaviour lives
The README passes an options object to the control, and the table of options is long enough that it is worth reading rather than skimming. The control inherits options from Leaflet controls generally.
The most consequential option is setView, which decides what the map does as the location updates. Its values are false, 'once', 'always', 'untilPan' and 'untilPanOrZoom', and the default is 'untilPanOrZoom', which means the map follows the user until they pan or zoom themselves. A developer who wants the map locked onto the user has to set it to 'always', and a developer who does not want the view to move at all has to set it to false. That single option decides the feel of the whole control.
Then there is keepCurrentZoomLevel, which takes true or a zoom range such as [13, 18], in which case the zoom is only kept when it is inside that range and the map zooms normally outside it. initialZoomLevel overrides that on activation, so a user who clicks the icon gets a chosen zoom even when the current level is being preserved. flyTo enables a smooth pan and zoom to the marker, off by default.
clickBehavior decides what a click on the control does, and its default object distinguishes three states: inView behaves as 'stop', outOfView behaves as 'setView', and inViewNotFollowing inherits 'inView'. The difference is that when the user is already inside the located area the map stays put, and when they are outside it, the control moves the map to them.
Two options deal with state rather than position. returnToPrevBounds saves the map bounds before centering and restores them when the control is disabled, and cacheLocation keeps the last known position after deactivation, so the user does not wait for a fresh API answer.
The compass has an iOS accuracy gate most wrappers skip
The compass is the part of this control with the most specific rule, and it is worth understanding before you enable it.
showCompass, on by default, draws the compass bearing on top of the location marker. The gate is compassAccuracyThreshold, whose default is 45 degrees and whose type is a number or false. It caps the iOS compass accuracy, taken from webkitCompassAccuracy, for displaying the compass. A value of -1, which means uncalibrated, is always rejected, and setting the option to false makes the compass always show when heading data is available at all.
So on iOS the behaviour depends on whether the device reports a calibrated heading. On an uncalibrated device the compass is suppressed rather than shown pointing in a wrong direction, and the threshold is what decides how precise the reading has to be before it is trusted. The CSS class used for the compass marker is CompassMarker, and the accuracy circle and the inner marker have their own class, LocationMarker, plus separate style properties, circleStyle, markerStyle and compassStyle, each documented as Path options. The style properties for the marker and the compass only work if your marker class supports setStyle, which is a small but real constraint when you swap in a custom marker.
There are also followCircleStyle and followMarkerStyle, described as changes applied to the accuracy circle and the inner marker while following, where only the changed properties need to be supplied.
Two CSS builds, because Leaflet and Mapbox style differently
The build scripts in package.json explain why the distribution carries more than one stylesheet. The JavaScript is bundled with rollup, while the CSS is compiled by lightningcss into two variants:
npm run build:css:leaflet && npm run build:css:mapboxThe leaflet variant compiles src/L.Control.Locate.css into both a readable L.Control.Locate.css and a minified L.Control.Locate.min.css, and the mapbox variant does the same for src/L.Control.Locate.mapbox.css, because the two map libraries do not share the same control styling conventions.
That is why the demo directory holds three separate examples, one each for Leaflet UMD, Leaflet ESM and Mapbox UMD, plus a style.css of its own. A separate demo/test-timeout.html file suggests the timeout path is also exercised somewhere in the demos.
The rest of the repository is conventional: a spec/ directory for tests, eslint.config.js and .prettierrc.json for linting and formatting, a rollup.config.js for the bundle, a scripts/ directory, and the CHANGELOG.md that the README links to. The CHANGELOG lives on the gh-pages branch, the same branch that carries the demo site and the source.
One packaging detail to note: the npm package ships dist, the README and the LICENSE, and the version in the manifest is 0.90.1 while the repository records no GitHub releases. So npm is where versions live, and the changelog on the branch is where the changes between them are described.
When writing the geolocation call yourself is the better option
The control is the wrong tool in a couple of recognisable cases.
If your page has no map, a locate button is pointless, and the browser geolocation API on its own is a few lines. If your map is built with a library other than Leaflet or Mapbox.js, this control does not apply, and the option table is not going to help.
The more interesting comparison is doing it by hand on a Leaflet map. Calling navigator.geolocation, drawing a circle and a marker, and updating them on each fix is a compact piece of code, and it gives you exactly the behaviour you want, including the awkward decisions the control makes for you. The difference in approach is that Leaflet.Locate ships a set of policies you would otherwise have to decide: when the view should follow and when it should stop, whether the zoom level is preserved inside a range, what a click on the button means depending on whether the user is inside or outside the located area, whether the previous bounds are restored when the control is switched off, and whether the iOS compass is accurate enough to draw.
The cost of that convenience is the dependency and the version churn. The README asks readers to check the changelog for breaking changes, the default branch is gh-pages, and the control sits on top of a browser API whose permission flow differs by browser. Pin a version, read the changelog before upgrading, and if you need behaviour the option table does not cover, remember that the control is a plugin you can also fork, since the source is in src/.
Editorial conclusion
Adopt Leaflet.Locate when your map needs a locate button whose behaviour you want to control precisely, since the options table covers the awkward cases such as untilPanOrZoom, keeping the zoom level inside a range and an iOS compass accuracy threshold that most wrappers skip. Do not adopt it for a one-line geolocation call on a page without a map, and read the changelog before you pin a version, because the README asks you to check it for breaking changes. Before upgrading, verify the two things that differ between builds: which stylesheet you import, since the CSS is compiled separately for Leaflet and for Mapbox, and whether you use the global L.control.locate factory or the ESM LocateControl class.
Frequently asked questions
How do I install Leaflet.Locate?
The README gives npm install leaflet.locatecontrol as the production install, and also offers the JsDelivr CDN or a download of the built files from the repository archive. For development it suggests git clone https://github.com/domoritz/leaflet-locatecontrol, where the source is in src/ and the built files are in dist/.
How do I add the locate control to a Leaflet map?
The README shows L.control.locate().addTo(map); to add the control, and L.control.locate(OPTIONS).addTo(map); to pass configuration. If you use a bundler or ES modules, import { LocateControl } from "leaflet.locatecontrol" and use new LocateControl() instead of the factory.
What does the setView option control in Leaflet.Locate?
It sets whether the map view follows the user's location as it updates. The accepted values are false, 'once', 'always', 'untilPan' and 'untilPanOrZoom', and the default is 'untilPanOrZoom'.
Why does the compass sometimes not appear in Leaflet.Locate?
The compassAccuracyThreshold option caps the allowed iOS compass accuracy in degrees, taken from webkitCompassAccuracy, and defaults to 45. A value of -1, meaning uncalibrated, is always rejected. Setting the option to false always shows the compass when heading data is available.
Which Leaflet and Mapbox versions is Leaflet.Locate tested against?
The README states it was tested with Leaflet 1.9.x and 2.x, and with Mapbox.js 3.3.1, in Firefox, Chrome and Safari. The plugin is MIT licensed and the npm package version in the manifest is 0.90.1.
Official sources
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.
[](https://hysenlabs.com/projects/domoritz-leaflet-locatecontrol)Star history chart for your README
[](https://hysenlabs.com/projects/domoritz-leaflet-locatecontrol)Was this useful?