Skip to main content

Overview

The useLocation hook provides a simple interface to access the device’s GPS coordinates using the browser’s Geolocation API. It includes automatic initial location detection, comprehensive error handling, and manual refresh capability.

Usage

import { useLocation } from '@/hooks/useLocation';

const { coords, error, loading, refresh } = useLocation();

Return Values

coords
object | null
Geographic coordinates object, or null if not yet available
error
string | null
User-friendly error message in Spanish, or null if no error
loading
boolean
true while fetching location, false when complete or failed
refresh
function
Function to manually trigger location refresh. Signature: () => void

Example

import { useLocation } from '@/hooks/useLocation';
import { getCurrentWeather } from '@/lib/api/weather';

export default function WeatherApp() {
  const { coords, error, loading, refresh } = useLocation();
  const [weather, setWeather] = useState(null);
  
  useEffect(() => {
    if (coords) {
      getCurrentWeather(coords.lat, coords.lon)
        .then(setWeather);
    }
  }, [coords]);
  
  if (loading) return <div>Detecting location...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!coords) return <div>Location unavailable</div>;
  
  return (
    <div>
      <p>Lat: {coords.lat}, Lon: {coords.lon}</p>
      {weather && <p>Temp: {weather.main.tempC</p>}
    </div>
  );
}

Error Handling

The hook provides user-friendly error messages in Spanish based on the Geolocation API error codes:
Error CodeMessage (Spanish)Meaning
1”Permiso denegado. Activa la ubicación en los ajustes.”User denied permission
2”Ubicación no disponible. Revisa tu señal o GPS.”Position unavailable
3”Tiempo de espera agotado. Reintenta.”Timeout exceeded
Unknown”Error desconocido”Other errors

Error State Example

const { error } = useLocation();

if (error) {
  // Example errors:
  // "Permiso denegado. Activa la ubicación en los ajustes."
  // "Ubicación no disponible. Revisa tu señal o GPS."
  // "Tiempo de espera agotado. Reintenta."
  console.error('Geolocation error:', error);
}

Configuration

The hook uses the following Geolocation API options:
enableHighAccuracy
boolean
default:true
Requests the most accurate position available. Critical for mobile devices.
timeout
number
default:15000
Maximum time (in milliseconds) to wait for position. Set to 15 seconds to accommodate slower mobile GPS connections.
maximumAge
number
default:0
Maximum age of cached position. Set to 0 to force fresh location data.

Customizing Options

If you need different configuration, modify the hook source:
const options: PositionOptions = {
  enableHighAccuracy: true,  // Best accuracy
  timeout: 15000,            // 15 seconds
  maximumAge: 0              // No cache
};

Behavior

Automatic Initial Detection

The hook automatically attempts to get the user’s location on mount:
useEffect(() => {
  getLocation(false); // Automatic, non-manual attempt
}, [getLocation]);

Manual Refresh

Calling refresh() triggers a new location request:
const { refresh } = useLocation();

// User clicks GPS button
refresh(); // Manually triggers location fetch
The difference between automatic and manual:
  • Automatic (isManual=false): Clears previous errors immediately
  • Manual (isManual=true): Preserves error state until new position is received, providing better user feedback

Browser Compatibility

The hook checks for Geolocation API support:
if (!navigator.geolocation) {
  setError("Tu navegador no soporta geolocalización");
  setLoading(false);
  return;
}

Supported Browsers

  • Chrome/Edge 5+
  • Firefox 3.5+
  • Safari 5+
  • Opera 10.6+
  • iOS Safari 3.2+
  • Android Browser 2.1+

Security Considerations

HTTPS Requirement

Geolocation API requires HTTPS in production:
  • https://yourdomain.com
  • http://localhost (development only)
  • http://yourdomain.com (will fail)

Permission Prompts

Browsers will prompt users for location permission:
  • First call triggers browser permission dialog
  • User choice is remembered for the domain
  • Denied permission results in error code 1

Privacy Best Practices

// Good: Explain why you need location
<div>
  <p>We need your location to show local weather</p>
  <button onClick={refresh}>Enable Location</button>
</div>

// Bad: Request without context
<button onClick={refresh}>Click Here</button>

Performance Tips

Avoiding Unnecessary Refreshes

const { coords, refresh } = useLocation();

// Good: Only refresh when user explicitly requests
<button onClick={refresh}>Update Location</button>

// Bad: Refreshing on every render
useEffect(() => {
  refresh(); // Infinite loop!
}, [coords]);

Caching Coordinates

const { coords } = useLocation();
const [savedCoords, setSavedCoords] = useState(null);

useEffect(() => {
  if (coords) {
    // Save to localStorage for offline use
    localStorage.setItem('lastCoords', JSON.stringify(coords));
    setSavedCoords(coords);
  }
}, [coords]);

Troubleshooting

Location Not Detected

  1. Check browser support: Ensure navigator.geolocation exists
  2. Verify HTTPS: Geolocation requires secure context
  3. Check permissions: User may have denied access
  4. Mobile GPS: Ensure location services are enabled in device settings

Timeout Errors

// Increase timeout for slow connections
const options: PositionOptions = {
  enableHighAccuracy: true,
  timeout: 30000, // 30 seconds instead of 15
  maximumAge: 0
};

Accuracy Issues

  • Desktop: Typically uses IP-based location (less accurate)
  • Mobile: Uses GPS when enableHighAccuracy: true
  • Indoors: GPS signal may be weak or unavailable

Integration Examples

With Weather API

import { useLocation } from '@/hooks/useLocation';
import { getCurrentWeather } from '@/lib/api/weather';

const { coords } = useLocation();
const [weather, setWeather] = useState(null);

useEffect(() => {
  if (coords) {
    getCurrentWeather(coords.lat, coords.lon)
      .then(data => setWeather(data));
  }
}, [coords]);

With Maps

import { useLocation } from '@/hooks/useLocation';
import { MapContainer, Marker } from 'react-leaflet';

const { coords } = useLocation();

return coords ? (
  <MapContainer center={[coords.lat, coords.lon]} zoom={13}>
    <Marker position={[coords.lat, coords.lon]} />
  </MapContainer>
) : null;

Build docs developers (and LLMs) love