The Globe component renders an interactive 3D map that visualizes visited countries and provides navigation controls. It uses Mapbox GL and integrates with the application’s state management to highlight selected countries.
Overview
The Globe component:
- Displays an interactive Mapbox GL map
- Highlights visited countries with orange overlay
- Supports dark/light theme based on system preferences
- Provides map navigation controls
- Responds to focus changes for zooming to specific countries
- Uses forwardRef for imperative map operations
Type Definitions
export interface MapForwardedRef {
isSourceLoaded: ForwardedRefFunction<MapRef['isSourceLoaded']>;
querySourceFeatures: ForwardedRefFunction<MapRef['querySourceFeatures']>;
}
Component Signature
export const Globe = memo(
forwardRef<MapForwardedRef>((_, ref) => {
// Component implementation
})
);
The component accepts no props but exposes map query methods through a ref.
Features
Theme Support
The globe automatically adapts to the user’s color scheme preference:
const prefersDark = useMatchMedia('(prefers-color-scheme: dark)');
<Map
mapStyle={prefersDark ? darkThemeUrl : lightThemeUrl}
// other props
/>
- Light theme:
mapbox://styles/mapbox/light-v11
- Dark theme:
mapbox://styles/mapbox/dark-v11
Country Highlighting
Visited countries are highlighted with an orange overlay:
const beenPaint: FillLayerSpecification['paint'] = {
'fill-color': '#fd7e14',
'fill-opacity': 0.6,
};
Building Extrusion
At high zoom levels (15+), buildings in visited countries are extruded:
<Layer
id={MapboxLayerKeys.Buildings}
type="fill-extrusion"
source="composite"
source-layer="building"
minzoom={15}
filter={buildingsFilter}
paint={buildingsPaint}
/>
Auto-Focus
The map automatically focuses on countries when the focusAtom changes:
useEffect(() => {
if (!focus?.bounds) {
return;
}
const { current: map } = internalRef;
map?.fitBounds(focus.bounds);
}, [focus]);
Map Configuration
Minimum zoom level to prevent excessive zoom-out
Mapbox API token from environment variable VITE_API_KEY_MAPBOX
Enable antialiasing for smoother rendering
Hide default attribution control
logoPosition
string
default:"bottom-right"
Position of Mapbox logo
Usage Example
import { Globe } from './components/globe';
import type { MapForwardedRef } from './components/globe';
import { useRef } from 'react';
function App() {
const globeRef = useRef<MapForwardedRef>(null);
const checkSource = () => {
const loaded = globeRef.current?.isSourceLoaded('countries');
console.log('Source loaded:', loaded);
};
return (
<div>
<Globe ref={globeRef} />
<button onClick={checkSource}>Check Source</button>
</div>
);
}
Layers
The globe uses multiple Mapbox layers:
1. Countries Layer
<Source
id={MapboxSourceKeys.Countries}
type="vector"
url="mapbox://mapbox.country-boundaries-v1"
/>
<Layer
id={MapboxLayerKeys.Been}
type="fill"
source={MapboxSourceKeys.Countries}
source-layer="country_boundaries"
beforeId="national-park"
filter={beenFilter}
paint={beenPaint}
/>
2. Buildings Layer
<Layer
id={MapboxLayerKeys.Buildings}
type="fill-extrusion"
source="composite"
source-layer="building"
minzoom={15}
filter={buildingsFilter}
paint={buildingsPaint}
/>
State Integration
The Globe component integrates with Jotai atoms:
selectedCountriesAtom - Array of ISO 3166 country codes to highlight
focusAtom - Current focus bounds for auto-zooming
Navigation Controls
<NavigationControl showCompass={false} />
Provides zoom in/out buttons without the compass control.
Dependencies
mapbox-gl - Mapbox GL library
react-map-gl - React wrapper for Mapbox GL
jotai - State management
react - Core React functionality
Environment Variables
Mapbox API access token for map tiles
Application mode (set to ‘test’ for test mode)
Source Code
Location: src/components/globe.tsx