Capabilities · API
Geolocation API
Published
navigator.geolocation reports the device’s position (latitude, longitude, accuracy radius, and optional altitude, heading, and speed) through a one-shot getCurrentPosition() or a continuous watchPosition(), after the browser has shown its own permission prompt. Nothing is returned synchronously: results and failures arrive in the callbacks you pass.
Every engine has shipped it since Chrome 5, Firefox 3.5, and Safari 5 (iOS 3). The secure-context requirement arrived later: Chrome 50, Firefox 55, and Safari 10 reject requests from http:// origins other than localhost with PERMISSION_DENIED while still exposing navigator.geolocation (BCD api.Geolocation.secure_context_required). GeolocationCoordinates.toJSON() is newer still: Chrome 126, Firefox 129, Safari 18.
Syntax
Section titled “Syntax”navigator.geolocation.getCurrentPosition(successCallback)navigator.geolocation.getCurrentPosition(successCallback, errorCallback)navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options)
navigator.geolocation.watchPosition(successCallback)navigator.geolocation.watchPosition(successCallback, errorCallback)navigator.geolocation.watchPosition(successCallback, errorCallback, options)
navigator.geolocation.clearWatch(watchId)getCurrentPosition() returns undefined. watchPosition() returns a long watch id greater than zero, invokes successCallback each time the position changes until clearWatch(watchId) is called, and returns 0 when the document is not fully active. No user activation is required, but Chrome logs a [Violation] when the first request is not inside one (see the observed callout).
Parameters
Section titled “Parameters”The two request methods share three parameters.
| Parameter | Type | Required | Description |
|---|---|---|---|
successCallback |
PositionCallback |
Yes | Receives a GeolocationPosition with coords (a GeolocationCoordinates) and timestamp (an EpochTimeStamp). |
errorCallback |
PositionErrorCallback? |
No | Receives a GeolocationPositionError with code and message. Without it, failures are silent. |
options |
PositionOptions |
No | The dictionary below. |
PositionOptions has three members, all optional.
| Member | Type | Required | Description |
|---|---|---|---|
enableHighAccuracy |
boolean |
No | Default false. A hint to use the most accurate source available (GPS on phones), at the cost of slower first fix and more battery. A cached position is reused only when its [[isHighAccuracy]] flag matches this value. |
timeout |
[Clamp] unsigned long |
No | Default 0xFFFFFFFF ms. Maximum time to wait once acquisition starts; time spent waiting for the permission prompt or for a hidden document to become visible does not count. 0 can fail immediately. |
maximumAge |
[Clamp] unsigned long |
No | Default 0. Accept a cached position no older than this many milliseconds; 0 forces a fresh fix. |
Exceptions
Section titled “Exceptions”Neither method throws. Failures reach errorCallback as a GeolocationPositionError whose code is one of three constants on the interface.
code |
Constant | Condition |
|---|---|---|
1 |
PERMISSION_DENIED |
The user or system denied the permission; the document is not allowed to use the geolocation Permissions Policy feature; or the environment is a non-secure context. |
2 |
POSITION_UNAVAILABLE |
The document is not fully active; or the underlying system failed to acquire a position. |
3 |
TIMEOUT |
options.timeout elapsed after acquisition started. |
message is implementation-defined text for developers, not for display. Chrome reports permission failures with User denied Geolocation and Permissions Policy failures with Geolocation has been disabled in this document by permissions policy. A PERMISSION_DENIED from the user persists until they change the setting themselves; repeated calls do not re-prompt.
Examples
Section titled “Examples”Each example checks "geolocation" in navigator and gives the user a manual way to supply a location when the API is missing or the request fails. Triggering the request from a button keeps the prompt expected and avoids Chrome’s violation warning.
A one-shot fix with a manual address fallback
Section titled “A one-shot fix with a manual address fallback”A timeout and a non-zero maximumAge keep the request from hanging on devices with no fix available. Every error code lands in the same fallback: showing the address field.
const addressField = document.querySelector("#address");
function useManualEntry(reason) { addressField.hidden = false; addressField.placeholder = reason;}
document.querySelector("#use-location").addEventListener("click", () => { if (!("geolocation" in navigator)) { useManualEntry("Enter your city"); return; } navigator.geolocation.getCurrentPosition( ({ coords }) => { showNearby(coords.latitude, coords.longitude, coords.accuracy); }, (error) => { const reasons = { 1: "Location access denied", 2: "Location unavailable", 3: "Location timed out" }; useManualEntry(reasons[error.code] ?? error.message); }, { enableHighAccuracy: false, timeout: 10_000, maximumAge: 60_000 }, );});coords.accuracy is the 95 % confidence radius in metres; a “near me” feature can treat anything under a few hundred metres as good enough without enableHighAccuracy.
Tracking a route and stopping when the view closes
Section titled “Tracking a route and stopping when the view closes”watchPosition() keeps the radio on until clearWatch() runs, so tie the watch to the lifetime of the UI that needs it.
let watchId = null;
function startTracking(onMove) { if (!("geolocation" in navigator)) return false; watchId = navigator.geolocation.watchPosition( (position) => onMove(position.coords), (error) => { stopTracking(); console.warn(`Tracking stopped: ${error.code} ${error.message}`); }, { enableHighAccuracy: true, maximumAge: 0 }, ); return watchId !== 0;}
function stopTracking() { if (watchId !== null) navigator.geolocation.clearWatch(watchId); watchId = null;}
document.querySelector("#start").addEventListener("click", () => startTracking(updateMap));document.querySelector("#stop").addEventListener("click", stopTracking);window.addEventListener("pagehide", stopTracking);A 0 return value means the document was not fully active when the watch was requested, so the function reports failure instead of waiting for an error callback that may not come.
Checking the permission state before prompting
Section titled “Checking the permission state before prompting”navigator.permissions.query({ name: "geolocation" }) reads the state without triggering a prompt, which lets the page show a “Use my location” button only when it can succeed or explain how to re-enable a denied permission.
async function locationButtonState() { if (!("geolocation" in navigator)) return "unsupported"; if (!navigator.permissions) return "unknown"; // request on click and handle the error const { state } = await navigator.permissions.query({ name: "geolocation" }); return state; // "granted" | "prompt" | "denied"}
const state = await locationButtonState();document.querySelector("#use-location").hidden = state === "unsupported" || state === "denied";document.querySelector("#denied-help").hidden = state !== "denied";The "unknown" branch matters for older WebViews that have geolocation but not navigator.permissions; there the page falls back to requesting on click and reading the error code.
See also
Section titled “See also”- The notification permission model, the same
granted/prompt/deniedmodel applied to another capability - Generic Sensor API, for orientation and motion rather than position
- Local Network Access, another permission-gated capability
- Geolocation API: getCurrentPosition() method (w3.org)
- Geolocation API: PositionOptions dictionary (w3.org)
- Geolocation API (developer.mozilla.org)
Specifications
| Specification | Status |
|---|---|
| Geolocation API | W3C |
| Geolocation API: getCurrentPosition() method | W3C |
| Geolocation API: watchPosition() method | W3C |
| Geolocation API: PositionOptions dictionary | W3C |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop) | Yes | 5 | high | source | — |
| Chrome (Android) | Yes | 18 | high | source | 1 |
| Edge (Desktop) | Yes | 12 | high | source | — |
| Firefox (Desktop) | Yes | 3.5 | high | source | 2 |
| Firefox (Android) | Yes | 4 | high | source | — |
| Safari (macOS) | Yes | 5 | high | source | — |
| Safari (iOS) | Yes | 3 | high | source | 3 |
| Samsung Internet | Yes | 1.0 | high | source | 4 |
| WebView (Android) | Yes | 4.4 | high | source | 5 |
- Derived by browser-compat-data mirroring from Chrome.
- GPSD (https://gpsd.gitlab.io/gpsd/index.html) (GPS daemon) support added in Firefox 3.6. WiFi-based location is provided by Google (privacy (https://support.mozilla.org/en-US/kb/does-firefox-share-my-location-websites)) or a custom provider (MLS instructions (https://wiki.mozilla.org/CloudServices/Location/Software)).
- browser-compat-data records support as ≤3: present in the earliest Safari on iOS release it tracks.
- Derived by browser-compat-data mirroring from Chrome Android.
- Derived by browser-compat-data mirroring from Chrome Android.