@octacondeveloper/locationiq-react
v1.0.1
Published
A comprehensive React component package for LocationIQ APIs
Maintainers
Readme
LocationIQ React Component Package
A comprehensive, production-ready React component library for integrating LocationIQ APIs into your React applications. Includes built-in components, custom hooks, and utility functions for geolocation, geocoding, routing, and map services.
Features
✨ Comprehensive API Coverage
- Forward Geocoding (address to coordinates)
- Reverse Geocoding (coordinates to address)
- Address Autocomplete
- Directions & Routing (car, bike, foot)
- Distance Matrix API
- Timezone API
- Nearby Points of Interest
- Static Maps
- Account Balance & Usage
🚀 Developer-Friendly
- TypeScript support with full type definitions
- Custom React hooks for all APIs
- Pre-built, ready-to-use components
- Zero configuration setup
- Comprehensive error handling
- Utility functions for common tasks
📦 Production-Ready
- Fully tested and reliable
- Efficient API call management
- Loading and error states built-in
- Responsive components
- ESM and CommonJS support
Installation
npm install @octacondeveloper/locationiq-react
# or
yarn add @octacondeveloper/locationiq-react
# or
pnpm add @octacondeveloper/locationiq-reactPrerequisites
- React 16.8.0 or higher
- Node.js 14.0.0 or higher
- A free or paid LocationIQ API key from https://locationiq.com
Quick Start
1. Get Your API Key
- Visit https://locationiq.com
- Sign up for a free account
- Get your API key from the dashboard
2. Initialize the Client
import { initializeClient } from '@octacondeveloper/locationiq-react';
// Initialize once in your app (e.g., in App.tsx)
initializeClient({
apiKey: 'your-api-key-here',
// baseURL: 'https://api.locationiq.com/v1' // optional, uses default if not specified
});3. Use Components or Hooks
import { GeocodingSearch, useGeocoding } from '@octacondeveloper/locationiq-react';
function MyComponent() {
return (
<GeocodingSearch
placeholder="Search for a location..."
onSelect={(result) => {
console.log(result);
}}
/>
);
}Core Components
1. GeocodingSearch
Convert addresses to geographic coordinates with search functionality.
import { GeocodingSearch } from '@octacondeveloper/locationiq-react';
export function SearchComponent() {
return (
<GeocodingSearch
placeholder="Enter an address..."
limit={10}
countrycodes="us,ca" // Optional: filter by country codes
onSelect={(result) => {
console.log(`Selected: ${result.address}`);
console.log(`Coordinates: ${result.lat}, ${result.lon}`);
}}
/>
);
}Props:
onSelect?: (result: GeocodingResult) => void- Callback when user selects a resultplaceholder?: string- Input placeholder textlimit?: number- Maximum results to display (default: 10)countrycodes?: string- Comma-separated country codes to filter resultsclassName?: string- CSS class name for styling
Returns:
GeocodingResultwith properties:address: string- Full addresslat: string- Latitudelon: string- Longitudeimportance?: number- Importance ratingboundingbox?: string[]- Bounding box coordinates
2. ReverseGeocodingDisplay
Convert coordinates to addresses. Includes built-in geolocation support.
import { ReverseGeocodingDisplay } from '@octacondeveloper/locationiq-react';
export function LocationDisplay() {
return (
<ReverseGeocodingDisplay
onResult={(address) => {
console.log(`Current location: ${address}`);
}}
/>
);
}Features:
- 📍 One-click "Get My Location" button
- Automatic geolocation using browser APIs
- Display current coordinates
- Full address information
Props:
onResult?: (address: string) => void- Callback with address stringclassName?: string- CSS class name
3. AddressAutocomplete
Real-time address suggestions as user types.
import { AddressAutocomplete } from '@octacondeveloper/locationiq-react';
export function AutocompleteDemo() {
const [address, setAddress] = useState('');
return (
<AddressAutocomplete
placeholder="Start typing an address..."
limit={5}
onSelect={(result) => {
setAddress(result.address);
console.log('Selected coordinates:', result.lat, result.lon);
}}
/>
);
}Props:
onSelect?: (result: AutocompleteResult) => void- Selection callbackplaceholder?: string- Input placeholderlimit?: number- Max suggestions to showclassName?: string- CSS class
Minimum Query Length: 2 characters
4. DirectionsViewer
Get directions and routing information between multiple points.
import { DirectionsViewer } from '@octacondeveloper/locationiq-react';
export function RoutingDemo() {
return (
<DirectionsViewer
profile="car" // 'car' | 'bike' | 'foot'
onRoutesLoaded={(routes) => {
routes.forEach((route, idx) => {
console.log(`Route ${idx + 1}: ${route.distance}m, ${route.duration}s`);
});
}}
/>
);
}Features:
- Support for multiple profiles (car, bike, foot)
- Detailed route information with legs and steps
- Distance and duration calculations
- Turn-by-turn directions
Example Output:
Routes Found: 2
Route 1
- Distance: 5.42 km
- Duration: 12m 45s
- Legs: 3
- Leg 1: 2.1 km (5m 30s)
- Leg 2: 1.8 km (4m 15s)
- Leg 3: 1.52 km (3m)5. StaticMap
Embed static map images with markers in your UI.
import { StaticMap } from '@octacondeveloper/locationiq-react';
export function MapDisplay() {
return (
<StaticMap
center={[40.7128, -74.006]} // NYC
zoom={15}
size={[600, 400]}
markers={[
{
lat: 40.7128,
lon: -74.006,
color: 'red',
size: 'medium',
},
{
lat: 40.7489,
lon: -73.968,
color: 'blue',
},
]}
alt="My Map"
/>
);
}Props:
center: [number, number]- Map center [lat, lon]zoom?: number- Zoom level (0-18, default: 15)size?: [number, number]- Map size [width, height] in pixelsmarkers?: Array<Marker>- Array of marker objectsclassName?: string- CSS classalt?: string- Image alt text
6. TimezoneDisplay
Display timezone information for a given location.
import { TimezoneDisplay } from '@octacondeveloper/locationiq-react';
export function TimeInfo() {
return (
<TimezoneDisplay
lat={40.7128}
lon={-74.006}
/>
);
}Displays:
- Timezone name (e.g., "America/New_York")
- Abbreviation (e.g., "EST", "EDT")
- UTC offset
- Current time
- Daylight Saving Time status
7. NearbyPOIViewer
Find and display points of interest near a location.
import { NearbyPOIViewer } from '@octacondeveloper/locationiq-react';
export function NearbyPlaces() {
return (
<NearbyPOIViewer
lat={40.7128}
lon={-74.006}
tag="cafe" // Optional: filter by amenity type
limit={20}
onPOISelect={(poi) => {
console.log(`Selected: ${poi.name}`);
console.log(`Type: ${poi.type}`);
}}
/>
);
}Props:
lat?: number- Latitudelon?: number- Longitudetag?: string- Amenity filter (cafe, restaurant, hotel, etc.)limit?: number- Max results (default: 20)onPOISelect?: (poi: NearbyPOIResult) => void- Selection callbackclassName?: string- CSS class
Common Tags:
cafe- Cafesrestaurant- Restaurantshotel- Hotelsparking- Parkinghospital- Hospitalspharmacy- Pharmaciesbank- Banksatm- ATMs
Custom Hooks
All custom hooks provide loading, error, and data states plus manual control functions.
useGeocoding
import { useGeocoding } from '@octacondeveloper/locationiq-react';
function SearchComponent() {
const { results, loading, error, geocode } = useGeocoding('Paris');
return (
<div>
{loading && <p>Searching...</p>}
{error && <p>Error: {error.message}</p>}
<ul>
{results.map((result) => (
<li key={result.place_id}>
{result.address}
</li>
))}
</ul>
<button onClick={() => geocode('London')}>
Search for London
</button>
</div>
);
}Hook Signature:
const {
results, // GeocodingResult[]
loading, // boolean
error, // Error | null
geocode // (query?: string) => Promise<void>
} = useGeocoding(query: string, autoSearch?: boolean)useReverseGeocoding
const {
result, // ReverseGeocodingResult | null
loading, // boolean
error, // Error | null
reverseGeocode // (lat?: number, lon?: number) => Promise<void>
} = useReverseGeocoding(lat?: number, lon?: number, autoFetch?: boolean)useAutocomplete
const {
results, // AutocompleteResult[]
loading, // boolean
error, // Error | null
autocomplete // (query?: string) => Promise<void>
} = useAutocomplete(query: string)useDirections
const {
routes, // RoutingRoute[]
loading, // boolean
error, // Error | null
getDirections // (coords?: [number, number][], profile?: 'car'|'bike'|'foot') => Promise<void>
} = useDirections(coordinates?: [number, number][])useMatrix
const {
matrix, // MatrixResult | null
loading, // boolean
error, // Error | null
getMatrix // (coords?: [number, number][], profile?: 'car'|'bike'|'foot') => Promise<void>
} = useMatrix(coordinates?: [number, number][])useTimezone
const {
timezone, // TimezoneResult | null
loading, // boolean
error, // Error | null
getTimezone // (lat?: number, lon?: number) => Promise<void>
} = useTimezone(lat?: number, lon?: number, autoFetch?: boolean)useNearbyPOI
const {
pois, // NearbyPOIResult[]
loading, // boolean
error, // Error | null
getNearby // (lat?: number, lon?: number, tag?: string) => Promise<void>
} = useNearbyPOI(lat?: number, lon?: number)useBalance
const {
balance, // { balance: number; currency: string } | null
loading, // boolean
error, // Error | null
getBalance // () => Promise<void>
} = useBalance()Direct API Access
For advanced use cases, use the LocationIQClient directly:
import { LocationIQClient, initializeClient, getClient } from '@octacondeveloper/locationiq-react';
// Initialize
initializeClient({ apiKey: 'your-key' });
// Get the client instance
const client = getClient();
// Use any API
const results = await client.geocode({ q: 'New York' });
const address = await client.reverseGeocode({ lat: 40.7128, lon: -74.0060 });
const routes = await client.getDirections({
coordinates: [[40.7128, -74.0060], [40.7489, -73.9680]],
profile: 'car',
});
// Get static map URL
const mapUrl = client.getStaticMapURL({
center: [40.7128, -74.0060],
zoom: 15,
size: [600, 400],
});Utility Functions
Coordinate and formatting utilities:
import { CoordinateUtils } from '@octacondeveloper/locationiq-react';
// Validation
CoordinateUtils.isValidCoordinate(40.7128, -74.0060); // true
// Distance calculation (Haversine formula)
const distance = CoordinateUtils.calculateDistance(
40.7128, -74.0060, // NYC
34.0522, -118.2437 // LA
);
// Result: ~3944.35 km
// Formatting
CoordinateUtils.formatCoordinate(40.7128, -74.0060, 2);
// Result: "40.71, -74.01"
// Parse from string
CoordinateUtils.parseCoordinateString("40.7128, -74.0060");
// Result: [40.7128, -74.0060]
// Bounding box calculations
const bbox = CoordinateUtils.getBoundingBox([
[40.7128, -74.0060],
[40.7489, -73.9680],
]);
// Result: { minLat: 40.7128, maxLat: 40.7489, minLon: -74.0060, maxLon: -73.9680 }
// Get center point
const center = CoordinateUtils.getCenterPoint([
[40.7128, -74.0060],
[40.7489, -73.9680],
]);
// Format distances and durations
CoordinateUtils.formatDistance(5420); // "5.42 km"
CoordinateUtils.formatDuration(3600); // "1h 0m 0s"
// Generate viewbox
CoordinateUtils.generateViewbox(40.7128, -74.0060, 40.7489, -73.9680);
// Result: "-74.0060,40.7128,-73.9680,40.7489"Complete Example Application
import React, { useState } from 'react';
import {
initializeClient,
GeocodingSearch,
DirectionsViewer,
StaticMap,
NearbyPOIViewer,
TimezoneDisplay,
CoordinateUtils,
} from '@octacondeveloper/locationiq-react';
// Initialize once
initializeClient({
apiKey: 'your-api-key-here',
});
export function LocationApp() {
const [startLocation, setStartLocation] = useState<[number, number] | null>(null);
const [endLocation, setEndLocation] = useState<[number, number] | null>(null);
return (
<div className="app">
<h1>LocationIQ Demo App</h1>
{/* Origin Search */}
<section>
<h2>From</h2>
<GeocodingSearch
placeholder="Search origin..."
onSelect={(result) => {
const coords: [number, number] = [
parseFloat(result.lat),
parseFloat(result.lon),
];
setStartLocation(coords);
}}
/>
{startLocation && (
<p>
Selected: {CoordinateUtils.formatCoordinate(...startLocation)}
</p>
)}
</section>
{/* Destination Search */}
<section>
<h2>To</h2>
<GeocodingSearch
placeholder="Search destination..."
onSelect={(result) => {
const coords: [number, number] = [
parseFloat(result.lat),
parseFloat(result.lon),
];
setEndLocation(coords);
}}
/>
{endLocation && (
<p>
Selected: {CoordinateUtils.formatCoordinate(...endLocation)}
</p>
)}
</section>
{/* Directions */}
{startLocation && endLocation && (
<>
<section>
<h2>Directions</h2>
<DirectionsViewer
profile="car"
onRoutesLoaded={(routes) => {
routes.forEach((route, idx) => {
const dist = CoordinateUtils.formatDistance(route.distance);
const dur = CoordinateUtils.formatDuration(route.duration);
console.log(`Route ${idx + 1}: ${dist} in ${dur}`);
});
}}
/>
</section>
{/* Map */}
<section>
<h2>Map</h2>
<StaticMap
center={CoordinateUtils.getCenterPoint([startLocation, endLocation])}
zoom={12}
size={[800, 400]}
markers={[
{ lat: startLocation[0], lon: startLocation[1], color: 'green' },
{ lat: endLocation[0], lon: endLocation[1], color: 'red' },
]}
/>
</section>
{/* Timezone for end location */}
<section>
<h2>Timezone at Destination</h2>
<TimezoneDisplay lat={endLocation[0]} lon={endLocation[1]} />
</section>
{/* Nearby POI */}
<section>
<h2>Nearby Places</h2>
<NearbyPOIViewer
lat={endLocation[0]}
lon={endLocation[1]}
limit={10}
/>
</section>
</>
)}
</div>
);
}
export default LocationApp;Advanced Usage
Custom Error Handling
import { useGeocoding } from '@octacondeveloper/locationiq-react';
function SearchWithErrorHandling() {
const { results, loading, error, geocode } = useGeocoding('');
const handleSearch = async (query: string) => {
try {
await geocode(query);
} catch (err) {
if (err instanceof Error) {
if (err.message.includes('Invalid API key')) {
console.error('API key is invalid');
} else if (err.message.includes('Rate limit')) {
console.error('Rate limit exceeded - try again later');
}
}
}
};
return (
<div>
{error && <div className="error-banner">{error.message}</div>}
<button onClick={() => handleSearch('London')}>Search</button>
</div>
);
}Debounced Autocomplete
import { useEffect, useState } from 'react';
import { useGeocoding } from '@octacondeveloper/locationiq-react';
function DebouncedSearch() {
const [query, setQuery] = useState('');
const [debouncedQuery, setDebouncedQuery] = useState('');
const { results } = useGeocoding(debouncedQuery);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedQuery(query);
}, 500); // 500ms debounce
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Type to search..."
/>
<ul>
{results.map((r) => (
<li key={r.place_id}>{r.address}</li>
))}
</ul>
</div>
);
}Caching API Responses
import { useGeocoding } from '@octacondeveloper/locationiq-react';
import { useRef } from 'react';
function CachedSearch() {
const cacheRef = useRef<Map<string, any>>(new Map());
const { results, geocode } = useGeocoding('');
const cachedGeocode = async (query: string) => {
if (cacheRef.current.has(query)) {
console.log('Using cached result');
return cacheRef.current.get(query);
}
const result = await geocode(query);
cacheRef.current.set(query, result);
return result;
};
return (
<button onClick={() => cachedGeocode('Paris')}>
Search (with cache)
</button>
);
}Error Handling
The package provides comprehensive error handling:
| Error | Reason | Solution |
|-------|--------|----------|
| Unauthorized: Invalid API key | API key is invalid or missing | Check your API key in dashboard |
| Rate limit exceeded | Too many requests | Implement request throttling or upgrade plan |
| Bad request | Invalid parameters | Check API documentation for parameter format |
| Network error | Connection issue | Check internet connection and API status |
Type Definitions
Full TypeScript support with exported types:
import {
GeocodingResult,
ReverseGeocodingResult,
AutocompleteResult,
RoutingRoute,
MatrixResult,
TimezoneResult,
NearbyPOIResult,
StaticMapRequest,
LocationIQConfig,
} from '@octacondeveloper/locationiq-react';Browser Support
- Chrome 58+
- Firefox 54+
- Safari 11+
- Edge 79+
Requires ES2015 (ES6) support.
Performance Optimization
Memoization
import { useMemo, useCallback } from 'react';
import { useGeocoding } from '@octacondeveloper/locationiq-react';
function OptimizedComponent() {
const memoizedQuery = useMemo(() => 'New York', []);
const { results } = useGeocoding(memoizedQuery);
const handleSelect = useCallback((result) => {
console.log(result);
}, []);
return <div>{/* ... */}</div>;
}Lazy Loading Maps
import { Suspense, lazy } from 'react';
const StaticMap = lazy(() => import('@octacondeveloper/locationiq-react').then(m => ({ default: m.StaticMap })));
function App() {
return (
<Suspense fallback={<div>Loading map...</div>}>
<StaticMap center={[40.7128, -74.0060]} />
</Suspense>
);
}Styling
Components use semantic CSS classes for easy customization:
/* Geocoding */
.locationiq-geocoding { }
.search-input-wrapper { }
.geocoding-input { }
.results-list { }
.result-item { }
/* Autocomplete */
.locationiq-autocomplete { }
.autocomplete-input { }
.suggestions-list { }
/* Directions */
.locationiq-directions { }
.routes-list { }
.route-item { }
/* Map */
.locationiq-static-map { }
/* Timezone */
.locationiq-timezone { }
.timezone-card { }
/* POI */
.locationiq-nearby-poi { }
.poi-list { }
.poi-item { }Migration Guides
From Google Maps API
LocationIQ provides similar functionality with better pricing:
// Google Maps
import { GoogleMap, Marker } from '@react-google-maps/api';
// LocationIQ
import { StaticMap } from '@octacondeveloper/locationiq-react';
// No API key in component - handled globally
<StaticMap center={[lat, lon]} markers={markers} />Troubleshooting
Q: Components not rendering
- Ensure
initializeClient()is called before rendering components - Check that API key is valid in LocationIQ dashboard
Q: Rate limit errors
- Implement debouncing for search inputs
- Consider upgrading your LocationIQ plan
Q: Geolocation not working
- HTTPS is required for browser geolocation
- User must grant location permission
- Check browser console for permission errors
Q: TypeScript errors
- Ensure TypeScript 4.0+ is installed
- Import types explicitly:
import type { GeocodingResult } from '@octacondeveloper/locationiq-react'
API Reference
See LocationIQ API Documentation for:
- Rate limits and quotas
- Detailed parameter documentation
- Response format specifications
- Authentication details
- Error codes
Contributing
Contributions welcome! Please see our Contributing Guidelines.
License
MIT © 2024 LocationIQ React Package
Support
Changelog
v1.0.0 (2024)
- Initial release
- All core APIs implemented
- Full TypeScript support
- Comprehensive component library
- Production-ready
Built with ❤️ for developers by the LocationIQ community
