Data

useGeoLocation

Browser geolocation (MDN, opens in a new tab) state.

Demo

Nothing runs until you click — your browser may then ask for permission. Denying it is part of the demo: the hook reports it via error instead of throwing.

Usage

use-geo-location.tsxtsx
import { useState } from "react";
import { useGeoLocation } from "hookli";

function Coordinates() {
  const { location, error } = useGeoLocation();

  if (error) return <p>{error.message}</p>;
  if (!location) return <p>Locating…</p>;

  const { latitude, longitude } = location.coords;
  return <p>{latitude.toFixed(4)}, {longitude.toFixed(4)}</p>;
}

export function Demo() {
  const [asked, setAsked] = useState(false);

  // The hook may prompt for permission as soon as it mounts —
  // keep it unmounted until a user gesture.
  if (!asked) {
    return <button onClick={() => setAsked(true)}>Where am I?</button>;
  }
  return <Coordinates />;
}

API

useGeoLocation.d.tsts
useGeoLocation(): { location: GeolocationPosition | null; error: GeolocationError | Error | null }

Parameters

This hook takes no parameters.

Returns

NameTypeDescription
locationGeolocationPosition | nullhookli's trimmed position type — just coords.latitude and coords.longitude. Null until the first reading arrives.
errorGeolocationError | Error | nullPermission denials, unsupported browsers and failed lookups all land here — read .message for display. Requesting starts on mount, so mount the hook behind a user gesture.

GeolocationPosition

hookli's trimmed reading — only the coordinates, not the full browser GeolocationPosition.

NameTypeDescription
coords.latitudenumberLatitude in decimal degrees.
coords.longitudenumberLongitude in decimal degrees.

GeolocationError

Shape of a permission or lookup failure.

NameTypeDescription
codenumberNumeric error code mirrored from the browser's GeolocationPositionError.
messagestringHuman-readable reason for the failure.

Hook

The implementation, straight from hookli. Copy it, or install the package.

src/hooks/useGeoLocation.hook.tstsx
import { useEffect, useState } from "react";

interface GeolocationError {
  code: number;
  message: string;
}

type GeolocationPosition = {
  coords: { latitude: number; longitude: number };
};

interface GeolocationState {
  location: GeolocationPosition | null;
  error: GeolocationError | Error | null;
}

export const useGeoLocation = (): GeolocationState => {
  const [location, setLocation] = useState<GeolocationPosition | null>(null);
  const [error, setError] = useState<GeolocationError | Error | null>(null);

  useEffect(() => {
    const getLocation = async () => {
      try {
        const position = await getCurrentPosition();
        setLocation({
          coords: {
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
          },
        });
      } catch (err) {
        setError(err as Error);
      }
    };

    if (navigator.geolocation) {
      navigator.permissions
        .query({ name: "geolocation" })
        .then((result) => {
          if (result.state === "granted") {
            getLocation();
          } else if (result.state === "prompt") {
            navigator.geolocation.getCurrentPosition(
              () => getLocation(),
              (err) => setError(err),
            );
          } else {
            setError(new Error("Geolocation permission denied"));
          }
        })
        .catch((err) => setError(err));
    } else {
      setError(new Error("Geolocation is not supported by this browser."));
    }
  }, []);

  return { location, error };
};

function getCurrentPosition(): Promise<globalThis.GeolocationPosition> {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
}

View source on GitHub

Related hooks