Rigid UI

useLocation

A React hook for location detection and reverse geocoding that provides easy access to user's current location with browser geolocation API.

Usage

Location Detector
Get your current location using browser geolocation
use-location-example.tsx
import { useLocation } from "@/hooks/use-location"

export default function MyComponent() {
  const {
    location,
    coordinates,
    isLoading,
    error,
    getCurrentLocation,
    getLocationFromCoordinates,
    clearLocation
  } = useLocation()

  return (
    <div className="space-y-4">
      <button
        onClick={getCurrentLocation}
        disabled={isLoading}
        className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
      >
        {isLoading ? 'Getting Location...' : 'Get Current Location'}
      </button>

      {location && (
        <div className="p-3 bg-green-100 rounded">
          <p>Current location: {location}</p>
          {coordinates && (
            <p className="text-sm text-gray-600">
              Coordinates: {coordinates.latitude}, {coordinates.longitude}
            </p>
          )}
          <button
            onClick={clearLocation}
            className="mt-2 px-3 py-1 text-sm bg-gray-500 text-white rounded"
          >
            Clear
          </button>
        </div>
      )}

      {error && (
        <div className="p-3 bg-red-100 text-red-800 rounded">
          {error}
        </div>
      )}
    </div>
  )
}

Installation

Install the useLocation hook using your preferred package manager.

npx shadcn@latest add https://rigidui.com/r/use-location.json

Features

The useLocation hook provides comprehensive location detection capabilities with a simple and intuitive API.

Browser Geolocation

Leverages the browser's native geolocation API to detect user's current position with high accuracy.

Reverse Geocoding

Converts latitude/longitude coordinates into human-readable location names using OpenStreetMap's Nominatim API.

Configurable Options

Customizable API endpoints and geolocation settings including timeout, accuracy, and cache options.

Error Handling

Comprehensive error handling for permission denials, timeouts, and network failures with descriptive messages.

Advanced Examples

Explore different configurations and use cases for the useLocation hook.

Basic Location Detection

Simple location detection with current location button and status display.

import { useLocation } from "@/hooks/use-location"
import { Button } from "@/components/ui/button"

export default function BasicLocationExample() {
  const { location, isLoading, error, getCurrentLocation, clearLocation } = useLocation()

  return (
    <div className="space-y-4">
      <Button onClick={getCurrentLocation} disabled={isLoading}>
        {isLoading ? 'Getting Location...' : 'Get Current Location'}
      </Button>

      {location && (
        <div className="p-3 bg-green-100 rounded">
          <p>📍 {location}</p>
          <Button variant="outline" size="sm" onClick={clearLocation}>
            Clear
          </Button>
        </div>
      )}

      {error && (
        <div className="p-3 bg-red-100 text-red-800 rounded">
          {error}
        </div>
      )}
    </div>
  )
}

Location with Coordinates

Display both location name and precise coordinates for better context.

import { useLocation } from "@/hooks/use-location"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"

export default function LocationWithCoordinatesExample() {
  const {
    location,
    coordinates,
    isLoading,
    error,
    getCurrentLocation
  } = useLocation()

  return (
    <div className="space-y-4">
      <Button onClick={getCurrentLocation} disabled={isLoading}>
        {isLoading ? 'Detecting...' : 'Get Location & Coordinates'}
      </Button>

      {location && coordinates && (
        <Card>
          <CardContent className="pt-4">
            <div className="space-y-2">
              <p className="font-medium">📍 {location}</p>
              <p className="text-sm text-muted-foreground">
                Lat: {coordinates.latitude.toFixed(6)},
                Lng: {coordinates.longitude.toFixed(6)}
              </p>
            </div>
          </CardContent>
        </Card>
      )}

      {error && (
        <Card className="border-red-200">
          <CardContent className="pt-4 text-red-600">
            <p>{error}</p>
          </CardContent>
        </Card>
      )}
    </div>
  )
}

Custom Coordinates Input

Allow users to input specific coordinates to get location information for any place.

import { useLocation } from "@/hooks/use-location"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export default function CustomCoordinatesExample() {
  const {
    location,
    isLoading,
    error,
    getLocationFromCoordinates
  } = useLocation()
  const [lat, setLat] = useState("")
  const [lng, setLng] = useState("")

  const handleGetLocation = () => {
    const latitude = parseFloat(lat)
    const longitude = parseFloat(lng)

    if (!isNaN(latitude) && !isNaN(longitude)) {
      getLocationFromCoordinates(latitude, longitude)
    }
  }

  return (
    <div className="space-y-4 max-w-md">
      <div className="grid grid-cols-2 gap-4">
        <div>
          <Label htmlFor="lat">Latitude</Label>
          <Input
            id="lat"
            value={lat}
            onChange={(e) => setLat(e.target.value)}
            placeholder="40.7128"
          />
        </div>
        <div>
          <Label htmlFor="lng">Longitude</Label>
          <Input
            id="lng"
            value={lng}
            onChange={(e) => setLng(e.target.value)}
            placeholder="-74.0060"
          />
        </div>
      </div>

      <Button
        onClick={handleGetLocation}
        disabled={isLoading || !lat || !lng}
        className="w-full"
      >
        {isLoading ? 'Getting Location...' : 'Get Location'}
      </Button>

      {location && (
        <div className="p-3 bg-blue-50 rounded">
          <p>📍 {location}</p>
        </div>
      )}

      {error && (
        <div className="p-3 bg-red-100 text-red-800 rounded">
          {error}
        </div>
      )}
    </div>
  )
}

Location Status Dashboard

Comprehensive dashboard showing all location states and capabilities.

Location Status
📍 Ready

• Browser geolocation required

• Uses OpenStreetMap for reverse geocoding

• High accuracy positioning enabled

import { useLocation } from "@/hooks/use-location"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"

export default function LocationDashboardExample() {
  const {
    location,
    coordinates,
    isLoading,
    error,
    getCurrentLocation,
    clearLocation
  } = useLocation()

  const getStatusBadge = () => {
    if (isLoading) return <Badge variant="secondary">🔄 Detecting...</Badge>
    if (error) return <Badge variant="destructive">❌ Error</Badge>
    if (location) return <Badge variant="default">✅ Located</Badge>
    return <Badge variant="outline">📍 Ready</Badge>
  }

  return (
    <Card className="max-w-md">
      <CardHeader>
        <CardTitle className="flex items-center justify-between">
          Location Status
          {getStatusBadge()}
        </CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="flex gap-2">
          <Button
            onClick={getCurrentLocation}
            disabled={isLoading}
            className="flex-1"
          >
            {isLoading ? 'Detecting...' : 'Get Location'}
          </Button>
          {location && (
            <Button variant="outline" onClick={clearLocation}>
              Clear
            </Button>
          )}
        </div>

        {location && (
          <div className="space-y-2 p-3 bg-green-50 rounded">
            <p className="font-medium">📍 {location}</p>
            {coordinates && (
              <p className="text-xs text-gray-600">
                {coordinates.latitude.toFixed(4)}, {coordinates.longitude.toFixed(4)}
              </p>
            )}
          </div>
        )}

        {error && (
          <div className="p-3 bg-red-50 text-red-700 rounded text-sm">
            {error}
          </div>
        )}

        <div className="text-xs text-gray-500 space-y-1">
          <p>• Browser geolocation required</p>
          <p>• Uses OpenStreetMap for reverse geocoding</p>
          <p>• High accuracy positioning enabled</p>
        </div>
      </CardContent>
    </Card>
  )
}

API Reference

Options

PropTypeDefault
apiUrl?
string
'https://nominatim.openstreetmap.org'
geolocationOptions?
PositionOptions
{ timeout: 10000, enableHighAccuracy: true }

Return Values

PropTypeDefault
location?
string
-
coordinates?
LocationCoordinates | null
-
isLoading?
boolean
-
error?
string | null
-
getCurrentLocation?
() => void
-
getLocationFromCoordinates?
(lat: number, lon: number) => Promise<void>
-
clearLocation?
() => void
-

Examples

Basic Usage

import { useLocation } from "@/hooks/use-location";

export default function LocationDisplay() {
  const { location, isLoading, getCurrentLocation } = useLocation();

  return (
    <div>
      <button onClick={getCurrentLocation} disabled={isLoading}>
        {isLoading ? "Detecting..." : "Get My Location"}
      </button>
      {location && <p>You are in: {location}</p>}
    </div>
  );
}

With Custom Options

import { useLocation } from "@/hooks/use-location";

export default function CustomLocationHook() {
  const { location, error, getCurrentLocation } = useLocation({
    apiUrl: "https://api.bigdatacloud.net/data/reverse-geocode-client",
    geolocationOptions: {
      timeout: 15000,
      enableHighAccuracy: false,
      maximumAge: 300000, // Cache for 5 minutes
    },
  });

  return (
    <div>
      <button onClick={getCurrentLocation}>Get Location</button>
      {location && <p>Location: {location}</p>}
      {error && <p className="error">Error: {error}</p>}
    </div>
  );
}

Manual Coordinate Input

import { useLocation } from "@/hooks/use-location";
import { useState } from "react";

export default function ManualLocation() {
  const { location, isLoading, getLocationFromCoordinates } = useLocation();
  const [lat, setLat] = useState("");
  const [lon, setLon] = useState("");

  const handleSubmit = () => {
    const latitude = parseFloat(lat);
    const longitude = parseFloat(lon);
    if (!isNaN(latitude) && !isNaN(longitude)) {
      getLocationFromCoordinates(latitude, longitude);
    }
  };

  return (
    <div className="space-y-4">
      <div className="flex gap-2">
        <input
          type="number"
          placeholder="Latitude"
          value={lat}
          onChange={(e) => setLat(e.target.value)}
          className="border rounded px-2 py-1"
        />
        <input
          type="number"
          placeholder="Longitude"
          value={lon}
          onChange={(e) => setLon(e.target.value)}
          className="border rounded px-2 py-1"
        />
        <button onClick={handleSubmit} disabled={isLoading}>
          Get Location
        </button>
      </div>
      {location && <p>Location: {location}</p>}
    </div>
  );
}

Error Handling

The hook provides detailed error messages for different failure scenarios:

  • Permission Denied: "Location access denied by user"
  • Position Unavailable: "Location information unavailable"
  • Timeout: "Location request timed out"
  • Network Error: Custom error message from the geocoding API
  • Browser Support: "Geolocation is not supported by this browser"

Types

export type LocationCoordinates = {
  latitude: number;
  longitude: number;
};

export interface UseLocationOptions {
  apiUrl?: string;
  geolocationOptions?: PositionOptions;
}

export interface UseLocationReturn {
  location: string;
  coordinates: LocationCoordinates | null;
  isLoading: boolean;
  error: string | null;
  getCurrentLocation: () => void;
  getLocationFromCoordinates: (lat: number, lon: number) => Promise<void>;
  clearLocation: () => void;
}

Notes

  • The hook uses OpenStreetMap's Nominatim API by default for reverse geocoding
  • Browser geolocation requires HTTPS in production environments
  • Users will be prompted to allow location access on first use
  • The hook automatically prevents duplicate requests when a location is already available
  • Coordinates are cached until clearLocation() is called or the component unmounts