useLocation
A React hook for location detection and reverse geocoding that provides easy access to user's current location with browser geolocation API.
Usage
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.
• 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
Prop | Type | Default |
---|---|---|
apiUrl? | string | 'https://nominatim.openstreetmap.org' |
geolocationOptions? | PositionOptions | { timeout: 10000, enableHighAccuracy: true } |
Return Values
Prop | Type | Default |
---|---|---|
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