Skip to content

Geolocation API: accessing the user's location

In one line: navigator.geolocation gives you the user’s physical position (latitude, longitude, and optional altitude/accuracy) after the browser obtains explicit permission — access is always user-initiated and gated by a permission prompt that the page cannot bypass.

navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude, accuracy } = position.coords;
console.log(`${latitude}, ${longitude}${accuracy}m)`);
},
(error) => {
// error.code: 1=PERMISSION_DENIED, 2=POSITION_UNAVAILABLE, 3=TIMEOUT
console.error(`Geolocation error ${error.code}: ${error.message}`);
},
{
enableHighAccuracy: false, // GPS vs network-based; battery trade-off
timeout: 10_000, // ms before the error callback fires
maximumAge: 60_000, // accept a cached position up to 60 s old
}
);

watchPosition returns a watch ID and fires the success callback each time the position changes. Call clearWatch(id) to stop tracking:

const watchId = navigator.geolocation.watchPosition(
(pos) => updateMap(pos.coords),
(err) => handleError(err)
);
// Later, when tracking is no longer needed:
navigator.geolocation.clearWatch(watchId);

Geolocation access is always gated by an explicit browser permission prompt. The browser asks the user “Allow [site] to know your location?” the first time getCurrentPosition or watchPosition is called. The spec requires that this prompt cannot be suppressed, pre-answered, or invoked without a browsing context.

  • The user can choose Allow, Block, or (in some browsers) Allow once.
  • A blocked grant surfaces in the error callback as error.code === 1 (PERMISSION_DENIED). Your code must handle this gracefully — show a manual location-entry fallback or a contextual explanation.
  • You can query the current permission state without prompting via the Permissions API: navigator.permissions.query({ name: 'geolocation' }) resolves to { state: 'granted' | 'denied' | 'prompt' }.

Geolocation is only available in secure contexts — HTTPS or localhost. On plain HTTP, navigator.geolocation is undefined regardless of browser.

No user-gesture requirement (but prompt is still shown)

Section titled “No user-gesture requirement (but prompt is still shown)”

Unlike some APIs, calling getCurrentPosition does not require an active user gesture. However, the browser will display the permission prompt, and users who were not expecting it will often deny it. For the best grant rate, call geolocation only when the user has clearly requested location-based functionality (e.g., by clicking a “Use my location” button).

Setting enableHighAccuracy: true requests GPS-quality position. This drains the battery faster and takes longer on cold start. Only set it when sub-50-meter accuracy is genuinely required (turn-by-turn navigation). For “near me” lookups, the default (false) network-based position is usually precise enough.

  • Never store raw coordinates beyond the task that required them.
  • Disclose location use clearly before triggering the prompt — users grant at higher rates when they understand why.
  • Prefer coarse location (enableHighAccuracy: false) unless precision is essential.

See /compatibility/ for current per-browser data.

Decision question Recommended action Rationale
One-time position lookup? getCurrentPosition with reasonable timeout and maximumAge. Avoids continuous tracking drain; maximumAge returns a recent cached fix instantly.
Real-time position tracking (map, navigation)? watchPosition + clearWatch when done. Keeps the fix current; always stop watching when the feature is not active.
Need to check permission state without prompting? navigator.permissions.query({ name: 'geolocation' }). Allows conditional UI without triggering an unexpected prompt.
User denied — what now? Show a manual address/city input; explain why location is useful. PERMISSION_DENIED is permanent until the user manually resets it in browser settings.
Accuracy vs battery trade-off? Default to enableHighAccuracy: false; only set true for navigation. GPS accuracy rarely justifies the battery cost for “near me” features.
  • Serve the page over HTTPS — Geolocation is unavailable on plain HTTP.
  • Call getCurrentPosition/watchPosition only when the user has clearly opted in to location functionality.
  • Handle all three error codes: PERMISSION_DENIED (1), POSITION_UNAVAILABLE (2), TIMEOUT (3).
  • Provide a non-location fallback (e.g., manual address input) for denied or unavailable cases.
  • Set a timeout and a sensible maximumAge on every call.
  • Always call clearWatch() when continuous tracking is no longer needed to avoid battery drain.
  • Query permission state first with the Permissions API if you want to conditionally show or hide a location feature.