Skip to main content

Viewport

The Viewport component provides a structured approach to organizing camera management logic into states and transitions between them. At any given time, the viewport is either idle, in a state (camera is being managed by a ViewportState), or transitioning between states.

Import

import { Viewport } from '@rnmapbox/maps';

Basic Usage

import { useRef } from 'react';
import { MapView, Viewport } from '@rnmapbox/maps';
import { Button } from 'react-native';

function MapWithViewport() {
  const viewportRef = useRef<Viewport>(null);

  const followUser = () => {
    viewportRef.current?.transitionTo({
      kind: 'followPuck',
      options: { zoom: 16, pitch: 45 },
    });
  };

  return (
    <>
      <MapView style={{ flex: 1 }}>
        <Viewport ref={viewportRef} />
      </MapView>
      <Button title="Follow User" onPress={followUser} />
    </>
  );
}

Props

transitionsToIdleUponUserInteraction

transitionsToIdleUponUserInteraction
boolean
default:"true"
Indicates whether the Viewport should idle when the MapView receives touch input.Set this property to false to enable building custom ViewportStates that can work simultaneously with certain types of gestures.
<Viewport transitionsToIdleUponUserInteraction={false} />

onStatusChanged

onStatusChanged
(event: ViewportStatusChangedEvent) => void
Subscribes to status changes. Called when the viewport status changes.Observers are notified of status changes asynchronously on the main queue. This means that by the time the notification is delivered, the status may have already changed again. This behavior is necessary to allow observers to trigger further transitions while avoiding out-of-order delivery of status changed notifications.
<Viewport
  onStatusChanged={(event) => {
    console.log('Viewport status changed:', event);
    console.log('From:', event.from);
    console.log('To:', event.to);
    console.log('Reason:', event.reason);
  }}
/>
ViewportStatusChangedEvent:
  • from: ViewportStatus - The previous status
  • to: ViewportStatus - The new status
  • reason: ViewportStatusChangeReason - Why the status changed
ViewportStatusChangeReason:
  • TransitionStarted - A transition has begun
  • TransitionSucceeded - A transition completed successfully
  • IdleRequested - Idle state was requested
  • UserInteraction - User interacted with the map

Methods

getState()

getState(): Promise<string>
Returns the current state of the viewport.
const state = await viewportRef.current?.getState();
console.log('Current state:', state);

idle()

idle(): Promise<void>
Transitions the viewport to an idle state, stopping any active state management.
await viewportRef.current?.idle();

transitionTo()

transitionTo(
  state: ViewportState,
  transition?: ViewportTransition
): Promise<boolean>
Transitions the viewport to a new state with an optional transition animation. Parameters:
state
ViewportState
required
The target viewport state. Can be either:FollowPuck State:
{
  kind: 'followPuck',
  options?: {
    zoom?: number | 'keep',
    pitch?: number | 'keep',
    bearing?: 'heading' | 'course' | number | 'keep',
    padding?: { top?: number, left?: number, bottom?: number, right?: number } | 'keep',
  }
}
Overview State:
{
  kind: 'overview',
  options?: {
    geometry: GeoJSON.Geometry,
    padding?: { top?: number, left?: number, bottom?: number, right?: number },
    bearing?: number,
    pitch?: number,
    animationDuration?: number,
  }
}
transition
ViewportTransition
Optional transition animation. Can be:Immediate Transition:
{ kind: 'immediate' }
Default Transition:
{ kind: 'default', maxDurationMs?: number }
Returns: Promise that resolves to true if the transition was successful. Examples:
// Follow user location
await viewportRef.current?.transitionTo({
  kind: 'followPuck',
  options: {
    zoom: 16,
    pitch: 45,
    bearing: 'heading',
  },
});

// Overview of geometry
await viewportRef.current?.transitionTo(
  {
    kind: 'overview',
    options: {
      geometry: {
        type: 'LineString',
        coordinates: [[-122.4, 37.8], [-122.5, 37.7]],
      },
      padding: { top: 50, bottom: 50, left: 50, right: 50 },
    },
  },
  { kind: 'default', maxDurationMs: 2000 }
);

Viewport States

FollowPuck State

Follows the user’s location (“puck”) with configurable camera settings.
options.zoom
number | 'keep'
The zoom level to use. If 'keep', zoom will not be modified.Default: DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM
options.pitch
number | 'keep'
The pitch (tilt) to use in degrees. If 'keep', pitch will not be modified.Default: DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH degrees
options.bearing
'heading' | 'course' | number | 'keep'
default:"'heading'"
Indicates how to obtain the bearing value:
  • 'heading' - Sets bearing to the heading of the device
  • 'course' - Sets bearing based on the direction of travel
  • number - Sets the camera bearing to a constant value
  • 'keep' - Bearing will not be modified
Note: On Android, 'heading' and 'course' both sync with the location puck’s bearing.
options.padding
object | 'keep'
default:"0 padding"
The viewport padding. If 'keep', padding will not be modified.Object shape: { top?: number, left?: number, bottom?: number, right?: number }

Overview State

Positions the camera to view a specific geometry.
options.geometry
GeoJSON.Geometry
required
The geometry that the viewport should frame in the camera view.
options.padding
object
default:"0 padding"
The padding to use when calculating the camera.Object shape: { top?: number, left?: number, bottom?: number, right?: number }
options.bearing
number
The bearing to use when calculating the camera.
options.pitch
number
The pitch to use when calculating the camera.
options.animationDuration
number
The length of the animation in seconds when the viewport starts updating the camera.

Examples

import { useRef } from 'react';
import { MapView, Viewport } from '@rnmapbox/maps';
import { Button, View } from 'react-native';

function FollowUserExample() {
  const viewportRef = useRef<Viewport>(null);

  const followUser = () => {
    viewportRef.current?.transitionTo({
      kind: 'followPuck',
      options: {
        zoom: 16,
        pitch: 45,
        bearing: 'heading',
      },
    });
  };

  const stopFollowing = () => {
    viewportRef.current?.idle();
  };

  return (
    <>
      <MapView style={{ flex: 1 }}>
        <Viewport ref={viewportRef} />
      </MapView>
      
      <View style={{ position: 'absolute', bottom: 20, left: 20 }}>
        <Button title="Follow User" onPress={followUser} />
        <Button title="Stop Following" onPress={stopFollowing} />
      </View>
    </>
  );
}

Platform Documentation

Build docs developers (and LLMs) love