553

useGeolocation

Hook that returns the current geolocation

browsermediumtest coverage
Locating...
import { useCopy, useGeolocation } from '@siberiacancode/reactuse';
import { CheckIcon, CopyIcon, MapPinIcon } from 'lucide-react';

const FALLBACK_COORDINATES = { latitude: 56.47414476814171, longitude: 84.95003125501695 };

const getMapEmbedUrl = (latitude: number, longitude: number) =>
  `https://maps.google.com/maps?q=${latitude},${longitude}&z=15&output=embed`;

const formatCoordinates = (latitude: number, longitude: number) =>
  `${latitude.toFixed(5)}, ${longitude.toFixed(5)}`;

const Demo = () => {
  const geolocation = useGeolocation();
  const { copy, copied } = useCopy();

  const hasLocation =
    !geolocation.value.loading &&
    !geolocation.value.error &&
    geolocation.value.latitude !== null &&
    geolocation.value.longitude !== null;

  const latitude = hasLocation ? geolocation.value.latitude! : FALLBACK_COORDINATES.latitude;
  const longitude = hasLocation ? geolocation.value.longitude! : FALLBACK_COORDINATES.longitude;

  return (
    <section className='flex w-full max-w-xl flex-col p-4'>
      <div className='bg-card relative h-[360px] overflow-hidden rounded-2xl shadow-sm'>
        <iframe
          allow='geolocation'
          className='size-full border-0'
          loading='lazy'
          referrerPolicy='no-referrer-when-downgrade'
          src={getMapEmbedUrl(latitude, longitude)}
          title='Map'
        />

        <div className='absolute top-3 right-3 flex items-center gap-2 rounded-full border border-white/10 bg-black/60 py-1.5 pr-1.5 pl-3 shadow-lg backdrop-blur-md'>
          {hasLocation && (
            <>
              <span className='font-mono text-xs font-semibold text-white tabular-nums'>
                {formatCoordinates(latitude, longitude)}
              </span>
              <button
                aria-label='Copy coordinates'
                data-size='icon-sm'
                data-variant='unstyled'
                type='button'
                onClick={() => copy(formatCoordinates(latitude, longitude))}
              >
                {copied ? (
                  <CheckIcon className='size-3.5 text-green-400' />
                ) : (
                  <CopyIcon className='size-3.5 text-white' />
                )}
              </button>
            </>
          )}

          {!hasLocation && (
            <>
              <MapPinIcon className='size-3.5 text-white/70' />
              <span className='pr-2 text-[10px] tracking-[0.15em] text-white/90 uppercase'>
                {geolocation.value.error ? 'Location unavailable' : 'Locating...'}
              </span>
            </>
          )}
        </div>
      </div>
    </section>
  );
};

export default Demo;
This hook uses navigator.geolocation browser api to provide enhanced functionality. Make sure to check for compatibility with different browsers when using this api

Installation

pnpm add @siberiacancode/reactuse

Usage

const { value, start, stop, watching } = useGeolocation(() => console.log('callback'));
// or
const { value, start, stop, watching } = useGeolocation({ immediately: false });

Type Declarations

export interface UseGeolocationValue {
  /** The accuracy of the last position update */
  accuracy: number | null;
  /** The altitude of the last position update */
  altitude: number | null;
  /** The altitude accuracy of the last position update */
  altitudeAccuracy: number | null;
  /** The error of the last position update */
  error: GeolocationPositionError | null;
  /** The heading of the last position update */
  heading: number | null;
  /** The latitude of the last position update */
  latitude: number | null;
  /** The loading state */
  loading: boolean;
  /** The longitude of the last position update */
  longitude: number | null;
  /** The speed of the last position update */
  speed: number | null;
  /** The timestamp of the last position update */
  timestamp: number | null;
}

export interface UseGeolocationReturn {
  /** The current geolocation state */
  value: UseGeolocationValue;
  /** Whether the watch is currently active */
  watching: boolean;
  /** Get the current position once (without starting a watch) */
  get: () => void;
  /** Start watching the position */
  start: () => void;
  /** Stop watching the position */
  stop: () => void;
}

export type UseGeolocationCallback = (position: GeolocationPosition) => void;

export interface UseGeolocationOptions extends PositionOptions {
  /** Whether to start watching immediately on mount (default: true) */
  immediately?: boolean;
  /** The callback function to be invoked when the geolocation changes */
  onChange?: UseGeolocationCallback;
  /** The callback function to be invoked when geolocation errors */
  onError?: (error: GeolocationPositionError) => void;
}

export interface UseGeolocation {
  (
    callback?: UseGeolocationCallback,
    options?: PositionOptions & { immediately?: boolean }
  ): UseGeolocationReturn;

  (options?: UseGeolocationOptions): UseGeolocationReturn;
}

API

Parameters

NameTypeDefaultNote
callbackUseGeolocationCallback-The callback function to be invoked when geolocation changes
params.immediatelybooleantrueStart watching immediately
params.enableHighAccuracyboolean-Enable high accuracy
params.maximumAgenumber-Maximum age
params.timeoutnumber-Timeout

Returns

UseGeolocationReturn

Parameters

NameTypeDefaultNote
optionsUseGeolocationOptions-Configuration options
options.immediatelybooleantrueStart watching immediately on mount
options.onChange(position: GeolocationPosition) => void-The callback function to be invoked when geolocation changes
options.onError(error: GeolocationPositionError) => void-The callback function to be invoked on geolocation error
options.enableHighAccuracyboolean-Enable high accuracy
options.maximumAgenumber-Maximum age
options.timeoutnumber-Timeout

Returns

UseGeolocationReturn

Contributors

ddebabinzzeroqs

Last updated on