Main map component.
import { MapView } from '@lugg/maps';
<MapView
style={{ flex: 1 }}
provider="google"
initialCoordinate={{ latitude: 37.7749, longitude: -122.4194 }}
initialZoom={12}
onCameraMove={(e) => console.log(e.nativeEvent)}
onCameraIdle={(e) => console.log(e.nativeEvent)}
>
{/* Markers, Polylines, etc. */}
</MapView>| Prop | Type | Default | Description |
|---|---|---|---|
provider |
MapProvider |
'apple' (iOS), 'google' (Android) |
Map provider |
mapType |
MapType |
'standard' |
Map display type |
mapId |
string |
- | Map style ID (Google) or configuration name (Apple) |
initialCoordinate |
Coordinate |
- | Initial camera coordinate |
initialZoom |
number |
10 |
Initial zoom level |
zoomEnabled |
boolean |
true |
Enable zoom gestures |
scrollEnabled |
boolean |
true |
Enable scroll/pan gestures |
rotateEnabled |
boolean |
true |
Enable rotation gestures |
pitchEnabled |
boolean |
true |
Enable pitch/tilt gestures |
compassEnabled |
boolean |
true |
Show compass on the map (rotate control on web) |
staticMode |
boolean |
false |
Render as a non-interactive static map. Ideal for list views |
staticKey |
string |
- | Stable identity for the static snapshot cache (iOS). See Static Maps |
edgeInsets |
EdgeInsets |
- | Map content edge insets |
userLocationEnabled |
boolean |
false |
Show current user location on the map |
userLocationButtonEnabled |
boolean |
false |
Show native my-location button (Android only) |
poiEnabled |
boolean |
true |
Show points of interest (Apple Maps only) |
poiFilter |
PoiFilter |
- | Filter POI categories (Apple Maps only) |
theme |
MapTheme |
'system' |
Map color theme |
insetAdjustment |
'automatic' | 'never' |
'never' |
Safe area inset adjustment behavior |
onPress |
(event: MapPressEvent) => void |
- | Called when the map is pressed |
onLongPress |
(event: MapPressEvent) => void |
- | Called when the map is long pressed |
onCameraMove |
(event: MapCameraEvent) => void |
- | Called when camera moves |
onCameraIdle |
(event: MapCameraEvent) => void |
- | Called when camera stops moving |
onReady |
() => void |
- | Called when map is loaded and ready |
Set staticMode to render a non-interactive map optimized for list views, where
mounting many live maps is expensive:
- Android (Google) - uses lite mode,
which renders a static bitmap instead of a live GL surface. Lite mode does
not support cloud-based styling, so
mapIdis ignored on lite static maps. Lite mode caps its bitmap at ~2048px per dimension, so larger views (e.g. full-screen maps) fall back to a full map with all gestures disabled. - iOS (Apple) - the base map is rendered with
MKMapSnapshotter, which loads entirely off the main thread (no live map view is ever created), so static maps keep loading while scrolling. Markers stay live views positioned over the base map; polylines, polygons, circles, and ground overlays are drawn onto it. Tile overlays are not supported. - iOS (Google) - markers and shapes render as live overlay views,
exactly like Apple static maps. Only the base map needs the SDK (which
has no async snapshotter): a live, tiles-only map warms up briefly under
the overlays and is replaced with a rendered image once tiles finish
loading, releasing the map's rendering resources. Warmup work scales
with how many rows mount at once - bound it with your list's
windowSize/initialNumToRender/maxToRenderPerBatch. - Web (Google) - gestures, POI clicks, keyboard interaction, and press events are disabled; the map itself stays live.
<Pressable onPress={() => openPlace(place)}>
<View style={{ height: 140 }} pointerEvents="none">
<MapView
staticMode
staticKey={place.id}
style={StyleSheet.absoluteFill}
initialCoordinate={place.coordinate}
initialZoom={14}
>
<Marker coordinate={place.coordinate} />
</MapView>
</View>
</Pressable>Notes:
staticModeis creation-time only and cannot be toggled after the map is created.- Ref methods (
moveCamera,fitCoordinates,setEdgeInsets) work like on a live map, minus animation -durationis ignored and the map re-renders at the final camera (iOS) or re-centers (Android). Map-setting prop updates (e.g.mapType) after the snapshot are still ignored on iOS. edgeInsetsshift the visible center like on a live map, so the coordinate centers in the inset viewport. Changing insets after the render re-renders the base map (iOS) or re-centers the camera (Android).- For taps, wrap the map in a
PressablewithpointerEvents="none"on the map container (as above) instead of relying ononPress. animatedpolylines render as complete static polylines, so the snapshot never freezes a mid-animation frame.- On iOS, markers are live views over the base map, revealed together with it - marker content that loads asynchronously (e.g. remote images) appears when ready, and marker prop updates still apply.
- While a static map loads, a theme-aware placeholder background is shown.
Set a
backgroundColorstyle on theMapViewto use your own. - Set
staticKey(e.g. your list item's id) to cache base map images across list recycling on iOS - scrolling back to a row reuses its image instead of rendering the map again (markers and shapes stay live and re-render from props). The camera and map settings are part of the cache key automatically. ChangingstaticKeydiscards the current image and re-renders. The cache is bounded by count and total memory. - Alternatively, keeping rows mounted (larger
windowSizewithremoveClippedSubviews) avoids re-rendering entirely at the cost of holding every row's snapshot in memory. - In long lists, limit how many rows mount at once (e.g.
windowSize,initialNumToRender,maxToRenderPerBatchonFlatList) - every mounted static map holds its snapshot image in memory.
| Value | Description |
|---|---|
'google' |
Google Maps |
'apple' |
Apple Maps (iOS only) |
| Value | Description |
|---|---|
'standard' |
Default map style |
'satellite' |
Satellite imagery |
'terrain' |
Terrain/topographic map |
'hybrid' |
Satellite imagery with labels |
'muted-standard' |
Muted standard style (Apple Maps only, falls back to standard on Google Maps) |
| Value | Description |
|---|---|
'light' |
Light appearance |
'dark' |
Dark appearance |
'system' |
Follow system appearance |
| Value | Description |
|---|---|
'never' |
No safe area inset adjustment |
'automatic' |
Automatically adjust for safe area insets |
interface PoiFilter {
mode?: 'including' | 'excluding'; // default: 'including'
categories: PoiCategory[];
}When mode is 'including', only the specified categories are shown. When 'excluding', all categories are shown except the specified ones.
| Value | Description | Availability |
|---|---|---|
'airport' |
Airports | iOS 13+ |
'amusement-park' |
Amusement parks | iOS 13+ |
'animal-service' |
Animal services | iOS 18+ |
'aquarium' |
Aquariums | iOS 13+ |
'atm' |
ATMs | iOS 13+ |
'automotive-repair' |
Automotive repair | iOS 18+ |
'bakery' |
Bakeries | iOS 13+ |
'bank' |
Banks | iOS 13+ |
'baseball' |
Baseball | iOS 18+ |
'basketball' |
Basketball | iOS 18+ |
'beach' |
Beaches | iOS 13+ |
'beauty' |
Beauty services | iOS 18+ |
'bowling' |
Bowling | iOS 18+ |
'brewery' |
Breweries | iOS 13+ |
'cafe' |
Cafes | iOS 13+ |
'campground' |
Campgrounds | iOS 13+ |
'car-rental' |
Car rental locations | iOS 13+ |
'castle' |
Castles | iOS 18+ |
'convention-center' |
Convention centers | iOS 18+ |
'distillery' |
Distilleries | iOS 18+ |
'ev-charger' |
EV charging stations | iOS 13+ |
'fairground' |
Fairgrounds | iOS 18+ |
'fire-station' |
Fire stations | iOS 13+ |
'fishing' |
Fishing | iOS 18+ |
'fitness-center' |
Fitness centers | iOS 13+ |
'food-market' |
Food markets | iOS 13+ |
'fortress' |
Fortresses | iOS 18+ |
'gas-station' |
Gas stations | iOS 13+ |
'go-kart' |
Go-kart | iOS 18+ |
'golf' |
Golf | iOS 18+ |
'hiking' |
Hiking | iOS 18+ |
'hospital' |
Hospitals | iOS 13+ |
'hotel' |
Hotels | iOS 13+ |
'kayaking' |
Kayaking | iOS 18+ |
'landmark' |
Landmarks | iOS 18+ |
'laundry' |
Laundry services | iOS 13+ |
'library' |
Libraries | iOS 13+ |
'mailbox' |
Mailboxes | iOS 18+ |
'marina' |
Marinas | iOS 13+ |
'mini-golf' |
Mini golf | iOS 18+ |
'movie-theater' |
Movie theaters | iOS 13+ |
'museum' |
Museums | iOS 13+ |
'music-venue' |
Music venues | iOS 18+ |
'national-monument' |
National monuments | iOS 18+ |
'national-park' |
National parks | iOS 13+ |
'nightlife' |
Nightlife venues | iOS 13+ |
'park' |
Parks | iOS 13+ |
'parking' |
Parking lots | iOS 13+ |
'pharmacy' |
Pharmacies | iOS 13+ |
'planetarium' |
Planetariums | iOS 18+ |
'police' |
Police stations | iOS 13+ |
'post-office' |
Post offices | iOS 13+ |
'public-transport' |
Public transport stations | iOS 13+ |
'restaurant' |
Restaurants | iOS 13+ |
'restroom' |
Restrooms | iOS 13+ |
'rock-climbing' |
Rock climbing | iOS 18+ |
'rv-park' |
RV parks | iOS 18+ |
'school' |
Schools | iOS 13+ |
'skate-park' |
Skate parks | iOS 18+ |
'skating' |
Skating | iOS 18+ |
'skiing' |
Skiing | iOS 18+ |
'soccer' |
Soccer | iOS 18+ |
'spa' |
Spas | iOS 18+ |
'stadium' |
Stadiums | iOS 13+ |
'store' |
Stores | iOS 13+ |
'surfing' |
Surfing | iOS 18+ |
'swimming' |
Swimming | iOS 18+ |
'tennis' |
Tennis | iOS 18+ |
'theater' |
Theaters | iOS 13+ |
'university' |
Universities | iOS 13+ |
'volleyball' |
Volleyball | iOS 18+ |
'winery' |
Wineries | iOS 13+ |
'zoo' |
Zoos | iOS 13+ |
import { useRef } from 'react';
import { MapView, MapViewRef } from '@lugg/maps';
const mapRef = useRef<MapViewRef>(null);
// Move camera to coordinate
mapRef.current?.moveCamera(
{ latitude: 37.7749, longitude: -122.4194 },
{ zoom: 15, duration: 500 }
);
// Fit coordinates in view
mapRef.current?.fitCoordinates(
[
{ latitude: 37.7749, longitude: -122.4194 },
{ latitude: 37.8049, longitude: -122.4094 },
],
{ padding: { top: 50, left: 50, bottom: 50, right: 50 }, duration: 500 }
);
// Set edge insets with animation
mapRef.current?.setEdgeInsets(
{ top: 0, left: 0, bottom: 200, right: 0 },
{ duration: 300 }
);Move the camera to a coordinate with optional zoom and animation duration. The current heading and pitch are preserved.
moveCamera(coordinate: Coordinate, options: MoveCameraOptions): void
interface MoveCameraOptions {
zoom: number;
duration?: number; // milliseconds, -1 for default
}Fit multiple coordinates in the visible map area. Resets heading to north (0°).
fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions): void
interface FitCoordinatesOptions {
padding?: EdgeInsets;
duration?: number; // milliseconds, -1 for default
}Programmatically update the map's edge insets with optional animation.
setEdgeInsets(edgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions): void
interface SetEdgeInsetsOptions {
duration?: number; // milliseconds, -1 for default animation, 0 for instant
}Called when the map is pressed or long pressed. Event includes the geographic coordinate and screen point.
interface PressEventPayload {
coordinate: Coordinate;
point: Point;
}Called continuously while the camera is moving.
interface CameraMoveEvent {
coordinate: Coordinate;
zoom: number;
dragging: boolean; // true if user is dragging
}Called when the camera stops moving.
interface CameraIdleEvent {
coordinate: Coordinate;
zoom: number;
}