useGeolocation

useGeolocation

A custom React hook for getting the user's geolocation from the Geolocation API (opens in a new tab).

Props

  • options (optional): an object with the following properties:
    • enableHighAccuracy (optional): a boolean that indicates whether to provide a more accurate position.
    • maximumAge (optional): a number that indicates the maximum age of the cached position in milliseconds.
    • timeout (optional): a number that indicates the maximum length of time in milliseconds to wait for a position.
  • callback (optional): a function that will be called when the position changes. The function will be passed the current coordinates object. -
  • isEnabled (optional): a boolean that indicates whether the hook should start watching for position changes.

Returns

An object with the following properties:

  • accuracy: a number that indicates the accuracy of the position in meters.

  • altitude: a number that indicates the altitude of the position in meters above the mean sea level

  • altitudeAccuracy: a number that indicates the accuracy of the altitude in meters.

  • heading: a number that indicates the direction of the device in degrees.

  • latitude: a number that indicates the latitude of the position.

  • longitude: a number that indicates the longitude of the position.

  • speed: a number that indicates the speed of the device in meters per second.

  • timestamp: a number that indicates the timestamp of the position.

  • error: an object that indicates an error if one occurred while trying to get the position.

The Hook

useGeolocation.ts
import { useState, useEffect, useCallback } from 'react'
 
type PositionError = GeolocationPositionError
 
interface GeolocationOptions {
  enableHighAccuracy?: boolean
  maximumAge?: number
  timeout?: number
}
 
interface Coordinates {
  accuracy: number | null
  altitude: number | null
  altitudeAccuracy: number | null
  heading: number | null
  latitude: number | null
  longitude: number | null
  speed: number | null
  timestamp: number | null
  error: PositionError | null
}
 
export const useGeolocation = (
  options: GeolocationOptions = {},
  callback?: (coordinates: Coordinates) => void,
  isEnabled = true
) => {
  const [coordinates, setCoordinates] = useState<Coordinates>({
    accuracy: null,
    altitude: null,
    altitudeAccuracy: null,
    heading: null,
    latitude: null,
    longitude: null,
    speed: null,
    timestamp: null,
    error: null
  })
  const updateCoordinates = useCallback(
    ({ coords, timestamp }: GeolocationPosition) => {
      const {
        accuracy,
        altitude,
        altitudeAccuracy,
        heading,
        latitude,
        longitude,
        speed
      } = coords
 
      setCoordinates({
        accuracy,
        altitude,
        altitudeAccuracy,
        heading,
        latitude,
        longitude,
        speed,
        timestamp,
        error: null
      })
 
      if (typeof callback === 'function') {
        callback({
          accuracy,
          altitude,
          altitudeAccuracy,
          heading,
          latitude,
          longitude,
          speed,
          timestamp,
          error: null
        })
      }
    },
    [callback]
  )
  const setError = useCallback((error: PositionError) => {
    setCoordinates({
      accuracy: null,
      altitude: null,
      altitudeAccuracy: null,
      heading: null,
      latitude: null,
      longitude: null,
      speed: null,
      timestamp: null,
      error
    })
  }, [])
  useEffect(() => {
    let watchId: number
 
    if (isEnabled && navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(updateCoordinates, setError)
      watchId = navigator.geolocation.watchPosition(
        updateCoordinates,
        setError,
        options
      )
    }
 
    return () => {
      if (watchId) {
        navigator.geolocation.clearWatch(watchId)
      }
    }
  }, [isEnabled, callback, options, setError, updateCoordinates])
 
  return coordinates
}

Usage

UseGeolocationDemo.tsx
import React from 'react'
import { useGeolocation } from './useGeolocation'
 
export const UseGeolocationDemo = () => {
  const coordinates = useGeolocation()
 
  return !coordinates.error ? (
    <ul>
      <li>Latitude: {coordinates.latitude}</li>
      <li>Longitude: {coordinates.longitude}</li>
      <li>Location accuracy: {coordinates.accuracy}</li>
      <li>Altitude: {coordinates.altitude}</li>
      <li>Altitude accuracy: {coordinates.altitudeAccuracy}</li>
      <li>Heading: {coordinates.heading}</li>
      <li>Speed: {coordinates.speed}</li>
      <li>Timestamp: {coordinates.timestamp}</li>
    </ul>
  ) : (
    <p>No geolocation, sorry.</p>
  )
}

Using PositionOptions

You can use the options parameter to pass PositionOptions to the watchPosition method of the Geolocation API. The PositionOptions object can be used to fine-tune the response from the watchPosition method.

UseGeolocationPositionDemo.tsx
const options = {
  enableHighAccuracy: true,
  maximumAge: 15000,
  timeout: 12000
}
const coordinates = useGeolocation(options)

In this example, the options object is passed to the useGeolocation hook. This will configure the watchPosition method to use high accuracy, return a cached position that is no older than 15 seconds, and wait for a maximum of 12 seconds to get a position.

Using a callback function

You can also pass a callback function as a second parameter to useGeolocation. The callback function will be called every time the position changes and will be passed the current coordinates object.

UseGeolocationCallbackDemo.tsx
import { useGeolocation } from './useGeolocation'
 
function Component() {
  const options = {
    enableHighAccuracy: true,
    maximumAge: 15000,
    timeout: 12000
  }
  const handlePositionChange = (coordinates) => {
    console.log(coordinates)
  }
  const coordinates = useGeolocation(options, handlePositionChange)
 
  return <div>Latitude: {coordinates.latitude}</div>
}

In this example, the handlePositionChange function will be called every time the user's position changes, and the current coordinates object will be passed to it as an argument. You can use this function to perform some action or update some state in your component every time the position changes.