MapView

July 8, 2026 · View on GitHub

Main map component.

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

PropTypeDefaultDescription
providerMapProvider'apple' (iOS), 'google' (Android)Map provider
mapTypeMapType'standard'Map display type
mapIdstring-Map style ID (Google) or configuration name (Apple)
initialCoordinateCoordinate-Initial camera coordinate
initialZoomnumber10Initial zoom level
zoomEnabledbooleantrueEnable zoom gestures
scrollEnabledbooleantrueEnable scroll/pan gestures
rotateEnabledbooleantrueEnable rotation gestures
pitchEnabledbooleantrueEnable pitch/tilt gestures
compassEnabledbooleantrueShow compass on the map (rotate control on web)
staticModebooleanfalseRender as a non-interactive static map. Ideal for list views
staticKeystring-Stable identity for the static snapshot cache (iOS). See Static Maps
edgeInsetsEdgeInsets-Map content edge insets
userLocationEnabledbooleanfalseShow current user location on the map
userLocationButtonEnabledbooleanfalseShow native my-location button (Android only)
poiEnabledbooleantrueShow points of interest (Apple Maps only)
poiFilterPoiFilter-Filter POI categories (Apple Maps only)
themeMapTheme'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 mapId is 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:

  • staticMode is 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 - duration is 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.
  • edgeInsets shift 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 Pressable with pointerEvents="none" on the map container (as above) instead of relying on onPress.
  • animated polylines 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 backgroundColor style on the MapView to 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. Changing staticKey discards the current image and re-renders. The cache is bounded by count and total memory.
  • Alternatively, keeping rows mounted (larger windowSize with removeClippedSubviews) 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, maxToRenderPerBatch on FlatList) - every mounted static map holds its snapshot image in memory.

Types

MapProvider

ValueDescription
'google'Google Maps
'apple'Apple Maps (iOS only)

MapType

ValueDescription
'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

ValueDescription
'light'Light appearance
'dark'Dark appearance
'system'Follow system appearance

InsetAdjustment

ValueDescription
'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

ValueDescriptionAvailability
'airport'AirportsiOS 13+
'amusement-park'Amusement parksiOS 13+
'animal-service'Animal servicesiOS 18+
'aquarium'AquariumsiOS 13+
'atm'ATMsiOS 13+
'automotive-repair'Automotive repairiOS 18+
'bakery'BakeriesiOS 13+
'bank'BanksiOS 13+
'baseball'BaseballiOS 18+
'basketball'BasketballiOS 18+
'beach'BeachesiOS 13+
'beauty'Beauty servicesiOS 18+
'bowling'BowlingiOS 18+
'brewery'BreweriesiOS 13+
'cafe'CafesiOS 13+
'campground'CampgroundsiOS 13+
'car-rental'Car rental locationsiOS 13+
'castle'CastlesiOS 18+
'convention-center'Convention centersiOS 18+
'distillery'DistilleriesiOS 18+
'ev-charger'EV charging stationsiOS 13+
'fairground'FairgroundsiOS 18+
'fire-station'Fire stationsiOS 13+
'fishing'FishingiOS 18+
'fitness-center'Fitness centersiOS 13+
'food-market'Food marketsiOS 13+
'fortress'FortressesiOS 18+
'gas-station'Gas stationsiOS 13+
'go-kart'Go-kartiOS 18+
'golf'GolfiOS 18+
'hiking'HikingiOS 18+
'hospital'HospitalsiOS 13+
'hotel'HotelsiOS 13+
'kayaking'KayakingiOS 18+
'landmark'LandmarksiOS 18+
'laundry'Laundry servicesiOS 13+
'library'LibrariesiOS 13+
'mailbox'MailboxesiOS 18+
'marina'MarinasiOS 13+
'mini-golf'Mini golfiOS 18+
'movie-theater'Movie theatersiOS 13+
'museum'MuseumsiOS 13+
'music-venue'Music venuesiOS 18+
'national-monument'National monumentsiOS 18+
'national-park'National parksiOS 13+
'nightlife'Nightlife venuesiOS 13+
'park'ParksiOS 13+
'parking'Parking lotsiOS 13+
'pharmacy'PharmaciesiOS 13+
'planetarium'PlanetariumsiOS 18+
'police'Police stationsiOS 13+
'post-office'Post officesiOS 13+
'public-transport'Public transport stationsiOS 13+
'restaurant'RestaurantsiOS 13+
'restroom'RestroomsiOS 13+
'rock-climbing'Rock climbingiOS 18+
'rv-park'RV parksiOS 18+
'school'SchoolsiOS 13+
'skate-park'Skate parksiOS 18+
'skating'SkatingiOS 18+
'skiing'SkiingiOS 18+
'soccer'SocceriOS 18+
'spa'SpasiOS 18+
'stadium'StadiumsiOS 13+
'store'StoresiOS 13+
'surfing'SurfingiOS 18+
'swimming'SwimmingiOS 18+
'tennis'TennisiOS 18+
'theater'TheatersiOS 13+
'university'UniversitiesiOS 13+
'volleyball'VolleyballiOS 18+
'winery'WineriesiOS 13+
'zoo'ZoosiOS 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 }
);

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 ().

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
}

Events

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;
}