I've been building web apps that need location data for years. And honestly? I've always dreaded the geolocation code. Not because the API is complicated—it's actually pretty simple—but because of everything around it. The permission prompts that fire before users even know why they need to share their location. The dead-end when someone clicks "Block" by accident. The awkward dance of trying to explain "please go to your browser settings and reset permissions."
Chrome 144 shipped something that made me genuinely excited: the <geolocation> element. It's a declarative, HTML-first approach to location permissions, and after playing with it, I'm convinced this is how permissions should have worked all along.
Why the Old Way Was Painful
Let's be honest about the traditional navigator.geolocation.getCurrentPosition() API. It works, but it creates problems.
The permission prompt fires whenever your JavaScript decides to call the function. Users see a popup before they've even clicked anything. They don't know why your app wants their location, and the natural instinct is to click "Block." I've watched user testing sessions where people blocked location access reflexively, then complained the app didn't work.
Once blocked, there's no recovery. Your code can't re-trigger the prompt. You're stuck showing a sad "please go to Settings > Privacy > Site Settings > Location" message that 95% of users will never follow.
The browser also has no way to know if the request came from genuine user intent or from some sketchy script trying to harvest location data on page load.
What the <geolocation> Element Does Differently
The core idea is simple: instead of JavaScript triggering the permission flow, the user triggers it by clicking a browser-controlled button. The browser renders a styled button with localized text (something like "Use Location" in English, "Standort verwenden" in German). When the user clicks it, they're providing an explicit signal: "Yes, I want to share my location."
Let's see the most basic version:
<geolocation>
<button onclick="requestLocationFallback()">Share Location</button>
</geolocation>
The content inside the <geolocation> element is fallback for browsers that don't support it yet. In Chrome 144+, the browser replaces this with its own trusted button.
Here's what happens when you wire it up with JavaScript:
const geoElement = document.getElementById("geoElement");
geoElement.addEventListener("location", () => {
const pos = geoElement.position;
if (pos) {
console.log(`Lat: ${pos.coords.latitude}, Lng: ${pos.coords.longitude}`);
} else if (geoElement.error) {
console.log(`Error: ${geoElement.error.message}`);
}
});
The above code is pretty simple to understand. We're listening for the location event on the element, then reading the position attribute directly from it. No callbacks, no separate success/error handlers—just straightforward property access.
The JavaScript Interface
The element exposes a clean interface that's worth understanding:
interface HTMLGeolocationElement extends HTMLElement {
readonly attribute GeolocationPosition? position;
readonly attribute GeolocationPositionError? error;
readonly attribute PermissionState permissionStatus;
attribute boolean autolocate;
attribute boolean watch;
attribute EventHandler onlocation;
attribute EventHandler onpromptaction;
attribute EventHandler onpromptdismiss;
}
The permissionStatus property is particularly useful. It tells you whether permission is granted, denied, or prompt—so you can build UI that responds appropriately without guessing.
Different Kinds of Location Tracking
The element supports two modes through attributes.
One-time location is the default. User clicks, location fires once:
<geolocation id="geo">
<button>Get My Location</button>
</geolocation>
Continuous tracking uses the watch attribute. It's like watchPosition() but declarative:
<geolocation id="tracker" watch>
<button>Track Me</button>
</geolocation>
Auto-locate is interesting. If you add the autolocate attribute and the user has already granted permission, the element will fetch location automatically on page load:
<geolocation id="geo" autolocate>
<button>Share Location</button>
</geolocation>
This is great for apps where location is central—like a delivery tracker where returning users have already opted in.
A More Complete Example
Let me show something closer to real-world usage. Here's a location card that displays coordinates:
<div class="location-card">
<h2>📍 Your Location</h2>
<div class="coordinates" id="coords">
Click the button to share your location
</div>
<geolocation id="geoElement">
<button onclick="fallbackLocation()">Share Location (Fallback)</button>
</geolocation>
</div>
<script>
const geoElement = document.getElementById("geoElement");
const coordsDisplay = document.getElementById("coords");
geoElement.addEventListener("location", () => {
const pos = geoElement.position;
if (pos) {
coordsDisplay.innerHTML = `
Lat: ${pos.coords.latitude.toFixed(6)}<br>
Lng: ${pos.coords.longitude.toFixed(6)}
`;
} else if (geoElement.error) {
coordsDisplay.textContent = `Error: ${geoElement.error.message}`;
}
});
// Fallback for unsupported browsers
function fallbackLocation() {
navigator.geolocation.getCurrentPosition(
(pos) => {
coordsDisplay.innerHTML = `
Lat: ${pos.coords.latitude.toFixed(6)}<br>
Lng: ${pos.coords.longitude.toFixed(6)}
`;
},
(err) => {
coordsDisplay.textContent = `Error: ${err.message}`;
},
);
}
</script>
Notice how the fallback button inside <geolocation> uses the traditional API. Progressive enhancement at its finest—old browsers get the old behavior, new browsers get the better UX.
Security That's Actually Built-In
The element has some clever security measures that I didn't have to think about (which is the point).
Visibility validation: The element must be fully visible in the viewport. No hiding it behind other elements or positioning it off-screen.
Movement detection: If you programmatically move the element, it's temporarily disabled for 500ms. This prevents clickjacking attacks where the element slides under the cursor right before a click.
Style constraints: You can't make the element invisible or heavily disguise it. The browser maintains control over its appearance.
Trusted events only: Only actual user clicks work. Synthetic JavaScript events won't trigger the permission flow.
I used to write custom code to prevent some of these attacks. Now the browser handles it.
Detecting Support
Since this is Chrome-only for now, you need feature detection:
function isGeolocationElementSupported() {
return "HTMLGeolocationElement" in window;
}
// Or the prototype check
function checkSupport() {
const testElement = document.createElement("geolocation");
return testElement instanceof HTMLGeolocationElement;
}
Then you can conditionally render different UI or rely on the fallback content.
Old vs. New: A Quick Comparison
Here's the traditional approach with all its ceremony:
async function requestLocation() {
try {
const permission = await navigator.permissions.query({
name: "geolocation",
});
if (permission.state === "denied") {
showPermissionDeniedUI();
return;
}
navigator.geolocation.getCurrentPosition(handleSuccess, handleError, {
enableHighAccuracy: true,
});
} catch (error) {
console.error("Permission API not supported");
}
}
And here's the new approach:
<geolocation id="geo" autolocate>
<button>Share Location</button>
</geolocation>
<script>
document.getElementById("geo").addEventListener("location", (e) => {
const { position, error } = e.target;
if (position) handleSuccess(position);
if (error) handleError(error);
});
</script>
Less code, better UX, built-in security. I'll take that trade every time.
Extra Points
The
onpromptdismissevent fires when a user closes the permission prompt without making a choice. This lets you show contextual help instead of leaving them confused.The
langattribute controls button text localization. The browser handles translation automatically, but you can override it if needed.Don't use
autolocateon first visit. It only works when permission is already granted, but the intent is for returning users—not to spam new visitors.The element is disabled inside fenced frames for security reasons. If you're building embedded widgets, keep this in mind.
Summary
The <geolocation> element is what I wish we'd had from the start. It turns a JavaScript-triggered, easily-blocked, no-recovery permission flow into a user-initiated, browser-trusted, properly-recoverable experience. Once other browsers adopt this pattern, geolocation in web apps will feel a lot less hostile to users—and a lot less painful for developers. :)
Top comments (0)