User Geolocation
Show the user's current GPS position on the map Each library has its own approach: here's how they compare.
Quick Recommendations
Leaflet — Makes the most sense when you want manual control over the geolocation UX with a custom accuracy circle and marker using the browser Geolocation API.
MapLibre GL JS — A strong fit when you want a built-in geolocate button with automatic tracking, heading indicator, and camera follow mode.
OpenLayers — Best when you need geolocation integrated with OpenLayers' coordinate projection system and feature-based position rendering.
MapTiler SDK — Ideal for the simplest setup: the built-in GeolocateControl works identically to MapLibre's with zero extra configuration.
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 | 13 | 14 | 27 | 12 |
Code Snippets
Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.
Leaflet13 linesShow code
const map = L.map('map').setView([50.0755, 14.4378], 5);
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);
map.locate({ setView: true, maxZoom: 16 });
map.on('locationfound', (e) => {
L.marker(e.latlng).addTo(map).bindPopup('You are here').openPopup();
L.circle(e.latlng, { radius: e.accuracy, color: '#2196F3', fillOpacity: 0.15 }).addTo(map);
});
map.on('locationerror', (e) => {
alert('Location access denied or unavailable.');
});MapLibre GL JS14 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: 12
});
map.addControl(new maplibregl.NavigationControl());
const geolocate = new maplibregl.GeolocateControl({
positionOptions: { enableHighAccuracy: true },
trackUserLocation: true,
showUserHeading: true
});
map.addControl(geolocate);
map.on('load', () => { geolocate.trigger(); });OpenLayers27 linesShow code
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>' }) })
],
view: new ol.View({ center: ol.proj.fromLonLat([0, 0]), zoom: 2 })
});
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Circle({ radius: 8, fill: new ol.style.Fill({ color: '#2196F3' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) })
}));
var accuracyFeature = new ol.Feature();
var locationLayer = new ol.layer.Vector({ source: new ol.source.Vector({ features: [accuracyFeature, positionFeature] }) });
map.addLayer(locationLayer);
var geolocation = new ol.Geolocation({
trackingOptions: { enableHighAccuracy: true },
projection: map.getView().getProjection(),
tracking: true
});
geolocation.on('change:position', function() {
var coords = geolocation.getPosition();
positionFeature.setGeometry(coords ? new ol.geom.Point(coords) : null);
map.getView().animate({ center: coords, zoom: 15 });
});
geolocation.on('change:accuracyGeometry', function() {
accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
});MapTiler SDK12 linesShow code
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
container: 'map',
style: maptilersdk.MapStyle.STREETS,
center: [15.0, 50.0],
zoom: 5
});
map.addControl(new maptilersdk.GeolocateControl({
positionOptions: { enableHighAccuracy: true },
trackUserLocation: true,
showUserHeading: true
}));Things to Know
Implementation-specific details that matter for user geolocation.
Uses the raw browser Geolocation API: you handle watchPosition, accuracy circle rendering, and error states manually.
GeolocateControl provides a one-line solution with tracking, accuracy circle, and fly-to animation; no manual Geolocation API needed.
Uses ol.Geolocation which wraps the browser API and emits position as ol.Coordinate in the map's projection (EPSG:3857).
GeolocateControl is identical to MapLibre's; the SDK adds no extra geolocation features beyond the inherited control.
Which Library to Use?
Display the user's current GPS position on a Leaflet map using the browser Geolocation API with an accuracy circle.
Makes the most sense when you want manual control over the geolocation UX with a custom accuracy circle and marker using the browser Geolocation API.
Show the user's current position on the map using the built-in maplibregl.GeolocateControl with tracking support.
A strong fit when you want a built-in geolocate button with automatic tracking, heading indicator, and camera follow mode.
Track and display the user's current position as a point feature on an OpenLayers map using the browser Geolocation API.
Best when you need geolocation integrated with OpenLayers' coordinate projection system and feature-based position rendering.
Show the user's current location on the map using the built-in maptilersdk.GeolocateControl with tracking.
Ideal for the simplest setup: the built-in GeolocateControl works identically to MapLibre's with zero extra configuration.
Build Maps with AI
Generate code like these examples using mapping library skills in your IDE.