@trackthatride/react-map
v1.0.0
Published
React components for real-time tracking maps using MapLibre GL JS
Maintainers
Readme
@trackthatride/react-map
React SDK for building web-based delivery tracking interfaces with interactive maps. Includes pre-built components and hooks for real-time tracking with MapLibre GL.
Installation
npm install @trackthatride/react-mapRequired Dependencies
npm install maplibre-glCSS Import
Import MapLibre GL CSS in your app:
import 'maplibre-gl/dist/maplibre-gl.css';Quick Start
import { TrackingMap, TrackingInfo } from '@trackthatride/react-map';
import 'maplibre-gl/dist/maplibre-gl.css';
function TrackingPage() {
const [trackingState, setTrackingState] = useState(null);
return (
<div>
<TrackingMap
trackingCode="TR123456789"
apiBaseUrl="https://your-app.replit.app"
onStateChange={setTrackingState}
style={{ height: '500px', width: '100%' }}
/>
{trackingState && (
<TrackingInfo
state={trackingState}
showDriverDetails
showETA
/>
)}
</div>
);
}Components
TrackingMap
Interactive map component with real-time driver tracking, pickup/delivery markers, and route display.
Props
interface TrackingMapProps {
trackingCode: string; // Required: Tracking code to display
apiBaseUrl?: string; // Optional: API base URL
style?: React.CSSProperties; // Optional: Container style
className?: string; // Optional: Container className
center?: [number, number]; // Optional: Initial map center [lng, lat]
zoom?: number; // Optional: Initial zoom level
mapStyle?: string; // Optional: MapLibre style URL
enableRealtime?: boolean; // Optional: Enable SSE updates (default: true)
showRoute?: boolean; // Optional: Show route line (default: true)
markers?: { // Optional: Custom marker icons
pickup?: string;
delivery?: string;
driver?: string;
};
onStateChange?: (state) => void; // Optional: State change callback
onMapLoad?: (map) => void; // Optional: Map load callback
onError?: (error: Error) => void; // Optional: Error callback
}Basic Usage
<TrackingMap
trackingCode="TR123456789"
apiBaseUrl="https://your-app.replit.app"
style={{ height: '600px', width: '100%' }}
/>Custom Map Style
<TrackingMap
trackingCode="TR123456789"
apiBaseUrl="https://your-app.replit.app"
mapStyle="https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY"
center={[-122.4194, 37.7749]} // San Francisco
zoom={12}
/>Custom Markers
<TrackingMap
trackingCode="TR123456789"
markers={{
pickup: '/icons/pickup-pin.png',
delivery: '/icons/delivery-pin.png',
driver: '/icons/car-icon.png'
}}
/>Event Handling
function TrackingPage() {
const [state, setState] = useState(null);
const [error, setError] = useState(null);
return (
<TrackingMap
trackingCode="TR123456789"
onStateChange={(newState) => {
console.log('Status:', newState.ride?.status);
setState(newState);
}}
onError={(err) => {
console.error('Map error:', err);
setError(err.message);
}}
onMapLoad={(map) => {
console.log('Map loaded successfully');
}}
/>
);
}TrackingInfo
Display formatted tracking information including ride details, driver info, and ETA.
Props
interface TrackingInfoProps {
state: TrackingSessionState; // Required: Current tracking state
style?: React.CSSProperties; // Optional: Container style
className?: string; // Optional: Container className
showDriverDetails?: boolean; // Optional: Show driver info (default: true)
showETA?: boolean; // Optional: Show ETA (default: true)
showConnectionStatus?: boolean; // Optional: Show connection status (default: false)
formatters?: { // Optional: Custom formatters
time?: (timestamp: string) => string;
status?: (status: string) => string;
eta?: (eta: string) => string;
};
}Basic Usage
<TrackingInfo
state={trackingState}
showDriverDetails
showETA
showConnectionStatus
/>Custom Formatting
<TrackingInfo
state={trackingState}
formatters={{
time: (timestamp) => new Date(timestamp).toLocaleTimeString(),
status: (status) => status.toUpperCase().replace('_', ' '),
eta: (eta) => {
const minutes = Math.ceil((new Date(eta) - new Date()) / 60000);
return `${minutes} minutes`;
}
}}
/>Custom Styling
<TrackingInfo
state={trackingState}
className="tracking-panel"
style={{
padding: '20px',
backgroundColor: '#f5f5f5',
borderRadius: '8px'
}}
/>Hooks
useTrackingSession
React hook for managing tracking sessions with automatic cleanup.
API
function useTrackingSession(config?: TrackingSessionConfig): {
state: TrackingSessionState | null;
isConnected: boolean;
connectionStatus: ConnectionStatus;
connect: (trackingCode: string) => Promise<void>;
disconnect: () => void;
refresh: () => Promise<void>;
error: Error | null;
}Basic Usage
import { useTrackingSession } from '@trackthatride/react-map';
function TrackingComponent() {
const {
state,
isConnected,
connectionStatus,
connect,
disconnect,
error
} = useTrackingSession({
baseUrl: 'https://your-app.replit.app'
});
useEffect(() => {
connect('TR123456789');
}, []);
if (error) {
return <div>Error: {error.message}</div>;
}
if (!isConnected) {
return <div>Connecting...</div>;
}
return (
<div>
<h1>Tracking: {state?.ride?.tracking_code}</h1>
<p>Status: {state?.ride?.status}</p>
{state?.driver && (
<p>Driver: {state.driver.firstName} {state.driver.lastName}</p>
)}
</div>
);
}Manual Connection Control
function TrackingComponent() {
const [trackingCode, setTrackingCode] = useState('');
const { state, connect, disconnect, isConnected } = useTrackingSession({
baseUrl: 'https://your-app.replit.app'
});
const handleConnect = async () => {
try {
await connect(trackingCode);
} catch (err) {
console.error('Connection failed:', err);
}
};
return (
<div>
<input
value={trackingCode}
onChange={(e) => setTrackingCode(e.target.value)}
placeholder="Enter tracking code"
/>
{!isConnected ? (
<button onClick={handleConnect}>Connect</button>
) : (
<button onClick={disconnect}>Disconnect</button>
)}
{state && <TrackingInfo state={state} />}
</div>
);
}Connection Status Display
function ConnectionIndicator() {
const { connectionStatus, state } = useTrackingSession({
baseUrl: 'https://your-app.replit.app'
});
useEffect(() => {
connect('TR123456789');
}, []);
const statusColors = {
connecting: 'orange',
connected: 'green',
reconnecting: 'yellow',
disconnected: 'red'
};
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<div
style={{
width: '10px',
height: '10px',
borderRadius: '50%',
backgroundColor: statusColors[connectionStatus]
}}
/>
<span>{connectionStatus}</span>
</div>
);
}Complete Examples
Full Tracking Page
import { useState } from 'react';
import { TrackingMap, TrackingInfo, useTrackingSession } from '@trackthatride/react-map';
import 'maplibre-gl/dist/maplibre-gl.css';
function FullTrackingPage() {
const [trackingState, setTrackingState] = useState(null);
return (
<div style={{ display: 'flex', height: '100vh' }}>
{/* Map Section */}
<div style={{ flex: 2 }}>
<TrackingMap
trackingCode="TR123456789"
apiBaseUrl="https://your-app.replit.app"
onStateChange={setTrackingState}
style={{ height: '100%', width: '100%' }}
enableRealtime
showRoute
/>
</div>
{/* Info Panel */}
<div style={{ flex: 1, overflowY: 'auto', padding: '20px' }}>
{trackingState ? (
<TrackingInfo
state={trackingState}
showDriverDetails
showETA
showConnectionStatus
/>
) : (
<p>Loading tracking information...</p>
)}
</div>
</div>
);
}
export default FullTrackingPage;Custom Tracking Dashboard
import { useEffect } from 'react';
import { useTrackingSession } from '@trackthatride/react-map';
import { Card, Badge, Progress } from '@/components/ui';
function TrackingDashboard({ trackingCode }) {
const { state, connect, error, connectionStatus } = useTrackingSession({
baseUrl: 'https://your-app.replit.app',
enableLogging: true
});
useEffect(() => {
if (trackingCode) {
connect(trackingCode);
}
}, [trackingCode]);
if (error) {
return <div className="error">Failed to load tracking: {error.message}</div>;
}
if (!state?.ride) {
return <div>Loading...</div>;
}
const { ride, driver, estimatedArrival } = state;
return (
<div className="dashboard">
<Card>
<h2>{ride.tracking_code}</h2>
<Badge status={ride.status}>{ride.status}</Badge>
<div className="details">
<h3>Customer</h3>
<p>{ride.customer_name}</p>
<p>{ride.phone_number}</p>
</div>
{driver && (
<div className="driver-info">
<h3>Driver</h3>
<p>{driver.firstName} {driver.lastName}</p>
<p>{driver.mobileNumber}</p>
<p>{driver.vehicleType} - {driver.vehiclePlate}</p>
</div>
)}
{estimatedArrival && (
<div className="eta">
<h3>Estimated Arrival</h3>
<p>{new Date(estimatedArrival).toLocaleString()}</p>
</div>
)}
<div className="connection">
Connection: <Badge status={connectionStatus}>{connectionStatus}</Badge>
</div>
</Card>
</div>
);
}Multiple Deliveries Tracker
import { useState } from 'react';
import { TrackingMap } from '@trackthatride/react-map';
function MultiDeliveryTracker() {
const [selectedTracking, setSelectedTracking] = useState('TR123456789');
const trackingCodes = ['TR123456789', 'TR987654321', 'TR555555555'];
return (
<div>
<div className="tracking-selector">
{trackingCodes.map(code => (
<button
key={code}
onClick={() => setSelectedTracking(code)}
className={selectedTracking === code ? 'active' : ''}
>
{code}
</button>
))}
</div>
<TrackingMap
key={selectedTracking}
trackingCode={selectedTracking}
apiBaseUrl="https://your-app.replit.app"
style={{ height: '600px', width: '100%' }}
/>
</div>
);
}Map Customization
Custom Map Styles
Use custom MapLibre styles for branded maps:
// OpenStreetMap (default)
const osmStyle = 'https://demotiles.maplibre.org/style.json';
// Dark theme
const darkStyle = 'https://api.maptiler.com/maps/dark/style.json?key=YOUR_KEY';
// Satellite
const satelliteStyle = 'https://api.maptiler.com/maps/hybrid/style.json?key=YOUR_KEY';
<TrackingMap
trackingCode="TR123456789"
mapStyle={darkStyle}
/>Accessing Map Instance
function AdvancedMap() {
const handleMapLoad = (map) => {
// Access MapLibre map instance
console.log('Map loaded:', map);
// Add custom layers
map.addLayer({
id: 'custom-layer',
type: 'circle',
source: 'points',
paint: {
'circle-radius': 8,
'circle-color': '#007cbf'
}
});
// Add custom controls
map.addControl(new maplibregl.FullscreenControl());
};
return (
<TrackingMap
trackingCode="TR123456789"
onMapLoad={handleMapLoad}
/>
);
}Type Definitions
All types from @trackthatride/realtime and @trackthatride/core are re-exported for convenience:
import {
// Components
TrackingMap,
TrackingInfo,
// Hooks
useTrackingSession,
// Types
TrackingMapProps,
TrackingInfoProps,
UseTrackingSessionResult,
TrackingSessionState,
Ride,
Driver
} from '@trackthatride/react-map';Best Practices
Always Import CSS: MapLibre GL requires its CSS to render properly.
import 'maplibre-gl/dist/maplibre-gl.css';Handle Loading States: Show loading indicators while connecting.
Error Handling: Always handle errors in
onErrorcallback.Cleanup: The hook automatically handles cleanup, but manual sessions need
destroy().Responsive Design: Set explicit heights for map containers.
<TrackingMap style={{ height: '400px', width: '100%' }} />Connection Feedback: Show connection status to users.
Mobile Optimization: Consider touch-friendly controls and responsive layouts.
Performance Tips
Memoize Callbacks: Use
useCallbackfor event handlers to prevent unnecessary re-renders.const handleStateChange = useCallback((state) => { setTrackingState(state); }, []);Debounce Updates: For high-frequency updates, debounce state updates.
Lazy Load Maps: Load map component only when needed.
const TrackingMap = lazy(() => import('@trackthatride/react-map'));
Troubleshooting
Map Not Displaying
Problem: Map container is empty
Solution:
- Ensure MapLibre CSS is imported
- Set explicit height on container
- Check console for MapLibre errors
Markers Not Showing
Problem: Map loads but markers don't appear
Solution:
- Verify ride has coordinates (
pickup_latitude,pickup_longitude, etc.) - Check that
showRouteprop is set correctly - Enable logging to debug
Connection Issues
Problem: Real-time updates not working
Solution:
- Verify
apiBaseUrlis correct - Check that tracking code exists
- Ensure server supports SSE
- Check
enableRealtimeprop
Browser Compatibility
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Full support
- IE11: Not supported (use polyfills)
License
MIT
Support
For support, please contact Track That Ride support or visit our documentation at /docs on your Track That Ride instance.
