MapView
The main map component. Renders Apple Maps or Google Maps with camera control, gestures, and static mode.
Usage
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>Props
| 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 |
Static Maps
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) -
MKMapSnapshotterrenders the base map off the main thread, so static maps keep loading while scrolling. With nonzeroedgeInsets, a temporaryMKMapViewrenders the image to keep Apple Maps attribution inside the inset viewport. The map view is released after capture. 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 without changing the zoom. On iOS, attribution also respects these insets. 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, and insets set withsetEdgeInsetsgive way to theedgeInsetsprop. The cache is bounded by count and total memory. - On iOS (Google), the base map is only captured and cached once the SDK
reports the map stable with all tiles loaded. If tiles fail (e.g.
offline), the live warmup map stays in place instead and renders again
the next time the row appears, so a bad render can't get stuck behind
staticKey. To force it, callreload()on the ref. - 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.
Types
MapProvider
| Value | Description |
|---|---|
'google' | Google Maps |
'apple' | Apple Maps (iOS only) |
MapType
| 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) |
MapTheme
| Value | Description |
|---|---|
'light' | Light appearance |
'dark' | Dark appearance |
'system' | Follow system appearance |
InsetAdjustment
| Value | Description |
|---|---|
'never' | No safe area inset adjustment |
'automatic' | Automatically adjust for safe area insets |
PoiFilter
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.
PoiCategory
| 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+ |
Ref Methods
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 }
);
// Load the map again (e.g. after a network outage)
mapRef.current?.reload();moveCamera
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
}fitCoordinates
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
}setEdgeInsets
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
}Insets set this way stay in place when the map re-renders or other props
change. The last write wins: a new edgeInsets prop value replaces them
(instantly, also during an animation), and a later setEdgeInsets call
replaces the prop.
moveCamera and fitCoordinates frame against the final insets, also while
an inset animation is still running.
reload
Load the map again, e.g. to recover from missing tiles after a network outage. None of the map SDKs can reload tiles in place, so:
- A static map re-renders its base map. On iOS the cached snapshot for its
staticKeyis discarded and the new render replaces the current image when ready; on Android the lite map is recreated. - A live map is recreated at its current coordinate and zoom. Heading and
pitch reset, and
onReadyfires again. Markers and overlays remain on the map after reload.
Reload preserves the map type and edge insets. On web, onReady fires once
for each new map instance. On iOS, an unfinished static reload restarts
when the map leaves the window and returns.
reload(): voidEvents
onPress / onLongPress
Called when the map is pressed or long pressed. Event includes the geographic coordinate and screen point.
interface PressEventPayload {
coordinate: Coordinate;
point: Point;
}onCameraMove
Called continuously while the camera is moving.
interface CameraMoveEvent {
coordinate: Coordinate;
zoom: number;
dragging: boolean; // true if user is dragging
}onCameraIdle
Called when the camera stops moving.
interface CameraIdleEvent {
coordinate: Coordinate;
zoom: number;
}