Geo
@ailuracode/alpine-geo
Reactive geolocation via the $store.geo store. Wraps the browser Geolocation API with one-shot requests and continuous position watching.
Install
pnpm add @ailuracode/alpine-geo @ailuracode/alpine-core @ailuracode/alpine-permissions alpinejsQuick start
import Alpine from "alpinejs";import geo from "@ailuracode/alpine-geo";
Alpine.plugin(geo);Alpine.start();Avoiding name collisions
If your application already owns a $store.geo or another toolkit plugin registers on that name, rename the integration surface without touching the controller:
Alpine.plugin(geoPlugin({ storeKey: "location", // → $store.location // magicKey follows storeKey by default → $location magicKey: "geoState", // explicit override → $geoState}));storeKey is the only argument most hosts need. magicKey moves independently only when both names must be freed. The exposed constants DEFAULT_GEO_STORE_KEY and DEFAULT_GEO_MAGIC_KEY keep the rename discoverable from TypeScript.
Store API
State
| Property | Type | Description |
|---|---|---|
latitude |
number | null |
Last known latitude in decimal degrees |
longitude |
number | null |
Last known longitude in decimal degrees |
accuracy |
number | null |
Accuracy radius in meters |
altitude |
number | null |
Altitude in meters above ellipsoid |
altitudeAccuracy |
number | null |
Altitude accuracy in meters |
heading |
number | null |
Direction of travel in degrees |
speed |
number | null |
Speed in meters per second |
timestamp |
number | null |
Position timestamp (Unix ms) |
error |
string | null |
Last error message |
errorCode |
number | null |
Geolocation error code (1 denied, 2 unavailable, 3 timeout) |
loading |
boolean |
true while a one-shot request() is pending |
watching |
boolean |
true while watch() is active |
Getters
| Getter | Type | Description |
|---|---|---|
hasPosition |
boolean |
true when latitude and longitude are available |
isSupported |
boolean |
true when navigator.geolocation exists |
isWatching |
boolean |
Alias for watching |
isLoading |
boolean |
Alias for loading |
hasError |
boolean |
true when error is set |
Actions
| Method | Returns | Description |
|---|---|---|
request(options?) |
Promise<boolean> |
One-shot position via getCurrentPosition |
watch(options?) |
boolean |
Start watchPosition; returns false if unsupported or already watching |
unwatch() |
boolean |
Stop active watch; returns false if none is active |
reset() |
boolean |
Clear position and error state without stopping a watch |
All actions accept optional PositionOptions: enableHighAccuracy, timeout, maximumAge.
HTML examples
<button @click="$store.geo.request({ enableHighAccuracy: true })" :disabled="!$store.geo.isSupported || $store.geo.isLoading"> Use my location</button>
<p x-show="$store.geo.hasPosition"> You are at <span x-text="$store.geo.latitude.toFixed(4)"></span>, <span x-text="$store.geo.longitude.toFixed(4)"></span> (±<span x-text="Math.round($store.geo.accuracy)"></span> m)</p>
<p x-show="$store.geo.hasError" x-text="$store.geo.error"></p><button x-show="!$store.geo.isWatching" @click="$store.geo.watch()"> Start tracking</button>
<button x-show="$store.geo.isWatching" @click="$store.geo.unwatch()"> Stop tracking</button>Notes
- Requires user permission in secure contexts (HTTPS or localhost)
request()andwatch()share the same reactive state; a successful update clears the previous errorreset()clears stored coordinates but does not stop an active watch — callunwatch()first if needed- Read-only environment access is not exposed as a magic; use the store for shared state and actions across components
Unified permissions adapter
import { permissionsPlugin } from "@ailuracode/alpine-permissions";import { createGeoPermissionAdapter } from "@ailuracode/alpine-geo";
Alpine.plugin( permissionsPlugin({ adapters: [createGeoPermissionAdapter()], }));Registry key: geolocation. See permissions.md.
License
MIT
