Skip to main content

Map & Location Hooks

Hooks for map provider abstraction, fetching item coordinates, and managing user geolocation with profile persistence.

useMapCoordinates

Fetches all item coordinates from the location index API for rendering markers on a map view. Uses React Query for caching.

useMapCoordinates(enabled?: boolean): UseMapCoordinatesReturn

Parameters

ParameterTypeDefaultDescription
enabledbooleantrueWhether to fetch coordinates. Set to false when the map view is not active.

Return Values

PropertyTypeDescription
coordinatesCoordinateEntry[]Array of coordinate entries for map markers
isLoadingbooleanWhether coordinates are being fetched
errorError | nullFetch error

CoordinateEntry Shape

FieldTypeDescription
slugstringItem slug for linking
latitudenumberLatitude coordinate
longitudenumberLongitude coordinate
citystring | nullCity name
countrystring | nullCountry name

Query Configuration

SettingValue
Query key['map-coordinates']
Stale time5 minutes
GC time10 minutes
EndpointGET /api/location/coordinates
import { useMapCoordinates } from '@/hooks/use-map-coordinates';

function MapView() {
const { coordinates, isLoading } = useMapCoordinates();

if (isLoading) return <MapSkeleton />;

return (
<Map>
{coordinates.map((coord) => (
<Marker
key={coord.slug}
lat={coord.latitude}
lng={coord.longitude}
label={coord.city ?? coord.slug}
/>
))}
</Map>
);
}

useMapProvider

Loads and initializes the configured map provider (Mapbox or Google Maps) based on location settings. Handles script loading, environment variable checking, and error states.

useMapProvider(): UseMapProviderResult & { providerInstance: IMapProvider | null }

Return Values

PropertyTypeDescription
provider'mapbox' | 'google'The configured provider name
providerInstanceIMapProvider | nullThe loaded provider instance
isConfiguredbooleanWhether the required API key env var is set
isLoadingbooleanWhether the provider script is loading
errorError | nullError during provider initialization
mapStylestringThe configured map style

Provider Detection

The hook checks environment variables to determine if a provider is configured:

ProviderEnvironment Variable
MapboxNEXT_PUBLIC_MAPBOX_ACCESS_TOKEN
Google MapsNEXT_PUBLIC_GOOGLE_MAPS_API_KEY

Provider Loading Flow

  1. Reads provider setting from useLocationSettings()
  2. Checks if the required env var is present
  3. Dynamically imports the provider implementation
  4. Calls provider.loadScript() to pre-load external SDK
  5. Returns the ready provider instance
import { useMapProvider } from '@/hooks/use-map-provider';

function MapContainer() {
const { provider, providerInstance, isConfigured, isLoading, error } =
useMapProvider();

if (!isConfigured) return <p>Map provider not configured</p>;
if (isLoading) return <Spinner />;
if (error) return <p>Failed to load map: {error.message}</p>;

return <div ref={(el) => providerInstance?.createMap(el, options)} />;
}

useMapProviderInstance

A convenience wrapper that returns only the instance-related properties.

useMapProviderInstance(): {
provider: IMapProvider | null;
isLoading: boolean;
error: Error | null;
}
const { provider, isLoading } = useMapProviderInstance();

if (provider) {
provider.createMap(containerRef.current, { center, zoom });
}

useUserLocation

Combines browser geolocation with the user's saved profile location. Browser coordinates take priority over profile coordinates. Includes mutations for saving and clearing the profile location.

useUserLocation(): UseUserLocationReturn

Return Values

PropertyTypeDescription
coordinatesCoordinates | nullBest available coordinates (browser or profile)
source'browser' | 'profile' | nullWhere the coordinates came from
profileLocationUserLocationProfile | nullSaved profile location data
isLoadingbooleanWhether profile fetch or browser geo is pending
requestBrowserLocation() => Promise<Coordinates | null>Prompt browser geolocation
saveProfileLocation(data) => Promise<UserLocationProfile>Save location to user profile
clearProfileLocation() => Promise<UserLocationProfile>Clear saved profile location
isSavingbooleanWhether a save/clear operation is in progress
errorError | nullProfile fetch error

UserLocationProfile Shape

FieldTypeDescription
defaultLatitudenumber | nullSaved latitude
defaultLongitudenumber | nullSaved longitude
defaultCitystring | nullSaved city name
defaultCountrystring | nullSaved country name
locationPrivacyLocationPrivacyPrivacy setting for location sharing

Coordinate Priority

  1. Browser coordinates (from useGeolocation) -- highest priority
  2. Profile coordinates (from database) -- fallback when browser geo is unavailable

Query Configuration

SettingValue
Query key['user', 'profile', 'location']
Stale time10 minutes
GC time30 minutes
RetryUp to 2 times; skips on "Unauthorized" errors
import { useUserLocation } from '@/hooks/use-user-location';

function LocationDisplay() {
const {
coordinates,
source,
requestBrowserLocation,
saveProfileLocation,
isSaving,
} = useUserLocation();

const handleSave = async () => {
if (coordinates) {
await saveProfileLocation({
defaultLatitude: coordinates.latitude,
defaultLongitude: coordinates.longitude,
});
}
};

return (
<div>
{coordinates ? (
<p>
Location ({source}): {coordinates.latitude.toFixed(4)},{' '}
{coordinates.longitude.toFixed(4)}
</p>
) : (
<button onClick={requestBrowserLocation}>
Share Location
</button>
)}
<button onClick={handleSave} disabled={isSaving || !coordinates}>
Save to Profile
</button>
</div>
);
}

Summary Table

HookPurposeSource File
useMapCoordinatesFetch item coordinates for map markersuse-map-coordinates.ts
useMapProviderLoad and initialize map provideruse-map-provider.ts
useMapProviderInstanceConvenience wrapper for provider instanceuse-map-provider.ts
useUserLocationBrowser + profile geolocation with CRUDuse-user-location.ts