|$ curl https://forge-ai.dev/api/markdown?path=docs/html/geolocation
$cat docs/geolocation-api.md
updated Recently·20 min read·published

Geolocation API

HTMLGeolocationAPIIntermediate
Introduction

The Geolocation API allows web applications to access the user's geographical location with their consent. It is part of the Navigator API and provides methods for one-time position requests, continuous position tracking, and manual position watching.

The API is available through navigator.geolocation and works over HTTPS only in modern browsers. It supports three methods: getCurrentPosition, watchPosition, and clearWatch.

Location data includes latitude, longitude, altitude, accuracy, heading, speed, and a timestamp. The browser determines the best source (GPS, Wi-Fi triangulation, IP geolocation) based on device capabilities and permissions.

Getting Position

The three core methods provide flexible access patterns for different use cases:

MethodDescriptionUse Case
getCurrentPositionOne-time position requestShow user location on map, weather lookup
watchPositionContinuous position updatesTurn-by-turn navigation, fitness tracking
clearWatchStop watching positionCleanup when leaving a tracking view
geolocation-basic.js
JavaScript
1// Get current position (one-time)
2navigator.geolocation.getCurrentPosition(
3 (position) => {
4 const { latitude, longitude } = position.coords;
5 console.log("Latitude:", latitude);
6 console.log("Longitude:", longitude);
7 },
8 (error) => {
9 console.error("Geolocation error:", error.message);
10 }
11);
12
13// Watch position (continuous tracking)
14const watchId = navigator.geolocation.watchPosition(
15 (position) => {
16 updateMapMarker(position.coords.latitude, position.coords.longitude);
17 updateSpeed(position.coords.speed);
18 },
19 (error) => handleLocationError(error),
20 { enableHighAccuracy: true, timeout: 5000, maximumAge: 0 }
21);
22
23// Stop watching when done
24navigator.geolocation.clearWatch(watchId);

info

The callback-based API is asynchronous. The success callback receives a GeolocationPosition object, and the error callback receives a GeolocationPositionError. The watchId returned by watchPosition is an integer used to stop tracking.
Position Object

The GeolocationPosition object contains a coords property with all location data and a timestamp property indicating when the position was acquired.

PropertyTypeDescription
coords.latitudenumberLatitude in decimal degrees (WGS84)
coords.longitudenumberLongitude in decimal degrees (WGS84)
coords.accuracynumberAccuracy radius in meters (lower = better)
coords.altitudenumber | nullAltitude in meters above sea level
coords.headingnumber | nullDirection of travel in degrees (0=N, 90=E)
coords.speednumber | nullSpeed in meters per second
timestampnumberDOMTimeStamp when position was acquired
position-object.js
JavaScript
1navigator.geolocation.getCurrentPosition((pos) => {
2 const c = pos.coords;
3
4 console.log("Position acquired at:", new Date(pos.timestamp).toISOString());
5 console.log("Latitude:", c.latitude.toFixed(6));
6 console.log("Longitude:", c.longitude.toFixed(6));
7 console.log("Accuracy:", c.accuracy, "meters");
8
9 if (c.altitude !== null) {
10 console.log("Altitude:", c.altitude.toFixed(1), "meters");
11 }
12
13 if (c.heading !== null) {
14 console.log("Heading:", c.heading.toFixed(1), "degrees");
15 }
16
17 if (c.speed !== null) {
18 console.log("Speed:", (c.speed * 3.6).toFixed(1), "km/h");
19 }
20});

best practice

Always check for null values on altitude, heading, and speed — they may not be available on all devices. The accuracy field is critical: a value of 100+ meters means the position is based on Wi-Fi or IP rather than GPS.
Error Handling

The error callback receives a GeolocationPositionError object with numeric codes and a human-readable message. Always handle all three error types gracefully.

ConstantCodeDescription
PERMISSION_DENIED1User denied the location permission
POSITION_UNAVAILABLE2Location provider returned no data
TIMEOUT3Position request exceeded the timeout
error-handling.js
JavaScript
1function handleLocationError(error) {
2 switch (error.code) {
3 case error.PERMISSION_DENIED:
4 showUI("Location access denied. Please enable in browser settings.");
5 console.warn("User denied geolocation permission");
6 break;
7
8 case error.POSITION_UNAVAILABLE:
9 showUI("Unable to determine location. Check GPS or network.");
10 console.warn("Position unavailable — GPS/Wi-Fi failure");
11 break;
12
13 case error.TIMEOUT:
14 showUI("Location request timed out. Try again.");
15 console.warn("Geolocation timeout exceeded");
16 break;
17
18 default:
19 showUI("An unknown error occurred: " + error.message);
20 }
21}
22
23function showUI(message) {
24 document.getElementById("location-status").textContent = message;
25}
26
27navigator.geolocation.getCurrentPosition(
28 onSuccess,
29 handleLocationError,
30 { timeout: 10000 }
31);

warning

The PERMISSION_DENIED error is permanent for the page session — once denied, subsequent calls will fail without re-prompting. Do not aggressively re-request permission. Provide a clear UI explaining why location is needed and guide the user to browser settings.
Options

The third parameter to both getCurrentPosition and watchPosition is an options object that controls accuracy, timeout, and caching behavior.

OptionDefaultDescription
enableHighAccuracyfalseUse GPS for best accuracy (more battery/power)
timeoutInfinityMax time (ms) to wait for a position
maximumAge0Accept cached position up to this age (ms)
geolocation-options.js
JavaScript
1// High accuracy for navigation (GPS required)
2const navOptions = {
3 enableHighAccuracy: true,
4 timeout: 15000,
5 maximumAge: 0, // no cached positions
6};
7
8// Battery-friendly for weather widget
9const weatherOptions = {
10 enableHighAccuracy: false,
11 timeout: 10000,
12 maximumAge: 300000, // accept 5-minute-old positions
13};
14
15// Fast approximate position
16const quickOptions = {
17 enableHighAccuracy: false,
18 timeout: 5000,
19 maximumAge: 600000, // accept 10-minute-old positions
20};
21
22navigator.geolocation.getCurrentPosition(
23 success, error, navOptions
24);

best practice

Use enableHighAccuracy: false by default to conserve battery. Only request high accuracy when the user explicitly engages a feature that needs GPS precision (e.g., navigation, nearby search). Set a reasonable timeout to avoid hanging the UI indefinitely.
Permissions API

The Permissions API allows you to check geolocation permission status without triggering a prompt. This is useful for adapting the UI before requesting a position.

permissions-api.js
JavaScript
1// Check geolocation permission state
2async function checkGeolocationPermission() {
3 const result = await navigator.permissions.query({
4 name: "geolocation"
5 });
6
7 console.log("Permission state:", result.state);
8 // "granted" | "denied" | "prompt"
9
10 result.addEventListener("change", () => {
11 console.log("Permission changed to:", result.state);
12 if (result.state === "granted") {
13 loadLocationFeatures();
14 }
15 });
16
17 return result.state;
18}
19
20// Usage
21const permissionState = await checkGeolocationPermission();
22
23switch (permissionState) {
24 case "granted":
25 // User already approved — call getCurrentPosition directly
26 loadMapData();
27 break;
28
29 case "prompt":
30 // Will show permission dialog — explain why location is needed first
31 showPermissionExplanation();
32 break;
33
34 case "denied":
35 // User previously denied — show instructions to re-enable
36 showLocationDisabledMessage();
37 break;
38}
🔥

pro tip

Use the Permissions API to drive UI state. When the state is "prompt", show a brief explanation of why you need location before calling getCurrentPosition — this increases permission acceptance rates. Listen for the change event to react when the user updates permissions in browser settings.
Use Cases

The Geolocation API powers a wide range of location-aware features across many application types:

Use CaseAccuracy NeededPattern
Maps & NavigationHigh (5-50m)watchPosition + enableHighAccuracy
WeatherLow (city-level)getCurrentPosition + maximumAge
Nearby SearchMedium (100m-1km)getCurrentPosition then search API
Fitness TrackingHigh (GPS)watchPosition + heading/speed
GeofencingMediumwatchPosition + distance calculation
Content PersonalizationLow (IP-level)getCurrentPosition without highAccuracy
geolocation-use-cases.js
JavaScript
1// Weather app — get city-level position
2async function getWeather() {
3 const pos = await new Promise((resolve, reject) => {
4 navigator.geolocation.getCurrentPosition(resolve, reject, {
5 enableHighAccuracy: false,
6 timeout: 10000,
7 maximumAge: 600000
8 });
9 });
10
11 const { latitude, longitude } = pos.coords;
12 const response = await fetch(
13 `https://api.weather.example/current?${latitude},${longitude}`
14 );
15 const weather = await response.json();
16 showWeatherUI(weather);
17}
18
19// Nearby places search
20async function searchNearby() {
21 const pos = await getCurrentPosition();
22 const { latitude, longitude } = pos.coords;
23
24 const places = await fetch(
25 `/api/places/nearby?lat=${latitude}&lng=${longitude}&radius=1000`
26 );
27 renderResults(places);
28}
29
30// Distance between two coordinates (Haversine formula)
31function haversineDistance(lat1, lon1, lat2, lon2) {
32 const R = 6371000; // Earth radius in meters
33 const toRad = (deg) => (deg * Math.PI) / 180;
34
35 const dLat = toRad(lat2 - lat1);
36 const dLon = toRad(lon2 - lon1);
37
38 const a =
39 Math.sin(dLat / 2) ** 2 +
40 Math.cos(toRad(lat1)) *
41 Math.cos(toRad(lat2)) *
42 Math.sin(dLon / 2) ** 2;
43
44 return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
45}

Live preview — simulated geolocation UI demonstrating the permission flow and coordinate display:

preview
Best Practices

Following these guidelines ensures a respectful, performant, and user-friendly geolocation experience:

Always request location in response to a user action (click/tap), never on page load
Explain why you need location before triggering the permission prompt — this increases consent rates
Handle permission denial gracefully — show a helpful message with instructions to re-enable
Use enableHighAccuracy: false by default; only enable GPS when the feature requires it
Set a reasonable timeout (5-15 seconds) to avoid hanging the UI
Use the Permissions API to check status before calling getCurrentPosition
Call clearWatch when leaving a tracking view to conserve battery
Cache position data with maximumAge to avoid unnecessary GPS wake-ups
Test on both HTTP (local dev) and HTTPS (production) — geolocation requires secure context
Always check for null values on altitude, heading, and speed before using them
Provide a fallback for when geolocation is unavailable (IP-based lookup, manual entry)
Respect user privacy — never store or transmit location without explicit consent

best practice

The most common mistake is requesting geolocation immediately on page load. Users who don't understand why location is needed will deny the request, and the browser may remember this decision permanently. Always pair location requests with a clear user-facing explanation and a specific user action (e.g., "Find nearby restaurants" button).
User Permission UX

The browser permission prompt is the gatekeeper for geolocation access. A well-designed permission UX significantly increases acceptance rates and reduces user friction.

permission-ux.js
JavaScript
1// Step 1: Check permission without prompting
2async function requestLocationFeature() {
3 const perm = await navigator.permissions.query({ name: "geolocation" });
4
5 if (perm.state === "granted") {
6 // Permission already given — proceed immediately
7 return getCurrentPosition();
8 }
9
10 if (perm.state === "denied") {
11 // Previously denied — show guidance, don't re-prompt
12 showLocationDisabledBanner();
13 return null;
14 }
15
16 // State is "prompt" — show explanation before requesting
17 showLocationExplanationDialog();
18}
19
20// Step 2: Show explanation dialog
21function showLocationExplanationDialog() {
22 const dialog = document.getElementById("location-prompt");
23 dialog.showModal();
24 // Dialog contains a "Allow Location" button that calls:
25 // navigator.geolocation.getCurrentPosition(...
26}
27
28// Step 3: Handle denial gracefully
29function onLocationDenied() {
30 document.getElementById("location-status").innerHTML = `
31 <div class="denied-banner">
32 <span>\26A0 Location access denied</span>
33 <p>Enable location in your browser settings to use nearby features.</p>
34 <button onclick="openBrowserSettings()">Open Settings</button>
35 </div>
36 `;
37}
38
39// Step 4: Handle re-enabling
40navigator.permissions.query({ name: "geolocation" }).then((perm) => {
41 perm.addEventListener("change", () => {
42 if (perm.state === "granted") {
43 reloadLocationFeatures();
44 }
45 });
46});
🔥

pro tip

Use a modal dialog with a clear explanation before the browser prompt appears. This "pre-permission" step lets users understand the value proposition in context. For example: "Show me restaurants near my location" is more compelling than "This website wants to know your location." Always provide a manual location entry fallback for users who decline.

Live preview — permission explanation dialog pattern:

preview
$Blueprint — Engineering Documentation·Section ID: HTML-34·Revision: 1.0