# Map viewport history

*Category:* Components
*Section:* Map
*Source:* https://uikit.developers.rio.cloud/#/components/mapViewportHistory
*Captured:* 2026-04-27T14:58:53.332Z

Viewport history stores relevant map movements and exposes a settings button that jumps back to the previous center and zoom. The button is only rendered when a previous viewport exists.

## Map viewport history

Use the buttons below to trigger programmatic map changes, pause or resume the moving truck asset, or pan and zoom manually. When the asset lock is active, the map follows the truck and the history button is hidden because returning to a previous viewport would immediately conflict with the locked asset.

### Example: Current center:N49° 8.106′ E10° 34.920′Zoom: 7Animation: offAsset lock: off

Terms of use © 1987–2026 HERE

50 km

Choose view
Map view
Satellite

Traffic conditions
Show traffic incidents

Select depot Select customer Reset map Enable animation Enable asset movement Current center: N49° 8.106′ E10° 34.920′ Zoom: 7 Animation: off Asset lock: off

#### React (tsx)

```tsx
import { useEffect, useRef, useState } from 'react';

import Button from '@rio-cloud/rio-uikit/Button';
import EventUtils, { type EventListenerMap } from '@rio-cloud/rio-uikit/EventUtils';
import Map from '@rio-cloud/rio-uikit/Map';
import MapLockMarkerButton from '@rio-cloud/rio-uikit/MapLockMarkerButton';
import MapPreviousViewportButton from '@rio-cloud/rio-uikit/MapPreviousViewportButton';
import MapSettings from '@rio-cloud/rio-uikit/MapSettings';
import MapTypeSettings from '@rio-cloud/rio-uikit/MapTypeSettings';
import Marker from '@rio-cloud/rio-uikit/Marker';
import Position from '@rio-cloud/rio-uikit/Position';
import SingleMapMarker from '@rio-cloud/rio-uikit/SingleMapMarker';
import type { MapEvent, Position as MapPosition } from '@rio-cloud/rio-uikit/mapTypes';

import { UIKIT_DEMO_CREDENTIALS_ARE_NOT_ALLOWED_IN_PROD_ENVIRONMENT } from '../uikit_demo_credentials';
import ButtonToolbar from '@rio-cloud/rio-uikit/ButtonToolbar';

const INITIAL_ZOOM = 7;

const initialPosition: MapPosition = { lat: 49.1351, lng: 10.582 };
const depotPosition: MapPosition = { lat: 48.7758, lng: 9.1829 };
const customerPosition: MapPosition = { lat: 49.4521, lng: 11.0767 };

const assetRoute: MapPosition[] = [
    { lat: 48.1351, lng: 11.582 },
    { lat: 48.144, lng: 11.596 },
    { lat: 48.151, lng: 11.612 },
    { lat: 48.159, lng: 11.628 },
    { lat: 48.167, lng: 11.645 },
    { lat: 48.154, lng: 11.626 },
    { lat: 48.142, lng: 11.604 },
];

const isSameViewport = (
    currentCenter: MapPosition,
    currentZoom: number,
    targetCenter: MapPosition,
    targetZoom: number
) =>
    Math.abs(currentCenter.lat - targetCenter.lat) < 0.0001 &&
    Math.abs(currentCenter.lng - targetCenter.lng) < 0.0001 &&
    Math.abs(currentZoom - targetZoom) < 0.1;

const MapViewportHistoryExample = () => {
    const [center, setCenter] = useState(initialPosition);
    const [zoom, setZoom] = useState(INITIAL_ZOOM);
    const [zoomAnimation, setZoomAnimation] = useState(false);
    const [assetPositionIndex, setAssetPositionIndex] = useState(0);
    const [isAssetMovementEnabled, setIsAssetMovementEnabled] = useState(false);
    const [isAssetLocked, setIsAssetLocked] = useState(false);

    const pendingViewportRef = useRef<{ center: MapPosition; zoom: number }>();

    const assetPosition = assetRoute[assetPositionIndex];

    useEffect(() => {
        if (!isAssetMovementEnabled) {
            return;
        }

        const intervalId = window.setInterval(() => {
            setAssetPositionIndex(currentIndex => (currentIndex + 1) % assetRoute.length);
        }, 5000);

        return () => window.clearInterval(intervalId);
    }, [isAssetMovementEnabled]);

    const setMapView = (position: MapPosition, nextZoom: number) => {
        pendingViewportRef.current = isSameViewport(center, zoom, position, nextZoom)
            ? undefined
            : { center: position, zoom: nextZoom };

        setCenter(position);
        setZoom(nextZoom);
    };

    const setMapCenter = (position: MapPosition) => {
        pendingViewportRef.current = isSameViewport(center, zoom, position, zoom)
            ? undefined
            : { center: position, zoom };

        setCenter(position);
    };

    useEffect(() => {
        if (isAssetLocked) {
            setMapCenter(assetPosition);
        }
    }, [assetPosition, isAssetLocked]);

    const eventListenerMap: EventListenerMap = {
        [EventUtils.MAP_VIEW_CHANGE_END]: (event: MapEvent) => {
            const target = event.currentTarget as H.Map;
            const updatedCenter = target.getCenter();
            const updatedZoom = target.getZoom();

            if (!updatedCenter) {
                return;
            }

            const pendingViewport = pendingViewportRef.current;
            if (pendingViewport) {
                if (!isSameViewport(updatedCenter, updatedZoom, pendingViewport.center, pendingViewport.zoom)) {
                    return;
                }

                pendingViewportRef.current = undefined;
            }

            setCenter(updatedCenter);
            setZoom(updatedZoom);
        },
    };

    const mapSettingsButtons = [
        <MapTypeSettings
            key='mapTypeSettings'
            tooltip='Change map type'
            dropdownHeaderText='Map views'
            defaultTypeLabel='Default view'
            truckTypeLabel='Truck view'
            terrainTypeLabel='Terrain view'
            satelliteTypeLabel='Satellite view'
            nightTypeLabel='Night view'
        />,
        <MapLockMarkerButton
            key='mapLockAssetButton'
            tooltip={isAssetLocked ? 'Unlock asset' : 'Lock asset'}
            isActive={isAssetLocked}
            onToggle={() => setIsAssetLocked(!isAssetLocked)}
        />,
    ];

    if (!isAssetLocked) {
        mapSettingsButtons.push(
            <MapPreviousViewportButton key='mapPreviousViewportButton' tooltip='Back to previous map position' />
        );
    }

    return (
        <>
            <Map
                credentials={UIKIT_DEMO_CREDENTIALS_ARE_NOT_ALLOWED_IN_PROD_ENVIRONMENT}
                center={center}
                zoom={zoom}
                height={400}
                eventListenerMap={eventListenerMap}
                zoomAnimation={zoomAnimation}
                mapSettings={<MapSettings options={mapSettingsButtons} />}
            >
                <Marker
                    position={assetPosition}
                    icon={<SingleMapMarker iconNames={['truck']} markerColor='bg-map-marker-asset' name='Asset' />}
                />
                <Marker
                    position={depotPosition}
                    icon={<SingleMapMarker iconNames={['warehouse']} markerColor='bg-map-marker-poi' name='Depot' />}
                />
                <Marker
                    position={customerPosition}
                    icon={<SingleMapMarker iconNames={['factory']} markerColor='bg-map-marker-route' name='Customer' />}
                />
            </Map>

            <ButtonToolbar className='margin-top-15'>
                <Button onClick={() => setMapView(depotPosition, 12)}>Select depot</Button>
                <Button onClick={() => setMapView(customerPosition, 13)}>Select customer</Button>
                <Button onClick={() => setMapView(initialPosition, INITIAL_ZOOM)}>Reset map</Button>
                <Button onClick={() => setZoomAnimation(!zoomAnimation)}>
                    {zoomAnimation ? 'Disable animation' : 'Enable animation'}
                </Button>
                <Button onClick={() => setIsAssetMovementEnabled(!isAssetMovementEnabled)}>
                    {isAssetMovementEnabled ? 'Pause asset movement' : 'Enable asset movement'}
                </Button>
                <div className='text-size-12 text-color-dark display-flex align-items-center gap-10'>
                    <span>Current center:</span>
                    <Position latitude={center.lat} longitude={center.lng} />
                    <span>Zoom: {Math.round(zoom)}</span>
                    <span>Animation: {zoomAnimation ? 'on' : 'off'}</span>
                    <span>Asset lock: {isAssetLocked ? 'on' : 'off'}</span>
                </div>
            </ButtonToolbar>
        </>
    );
};

export default MapViewportHistoryExample;
```

#### Props: Map

### Map

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| credentials | MapCredentials | — | HERE credentials used to initialize the map platform. |
| height | number | — | Height of the map container. |
| width | number | — | Width of the map container. |
| language | string | 'en' | Language used for HERE map labels and controls. |
| center | Position | — | Initial center position of the map. |
| zoom | number | — | Initial or controlled zoom level of the map. |
| boundingBox | BoundingBox | — | Bounding box that should be fitted by the map view. |
| disableMapEvents | boolean | — | Disables HERE map events entirely. |
| disableBehavior | boolean | false | Disables the default HERE map behavior such as pan and zoom interaction. |
| disableZoomMomentum | boolean | true | Disables HERE zoom momentum to reduce scroll inertia on zoom. |
| eventListenerMap | EventListenerMap | — | Event listeners attached to the HERE map instance. |
| hideMapSettings | boolean | — | Legacy prop for hiding map settings. |
| hideClusterSettings | boolean | false | Hides the cluster settings entry in the map settings UI. |
| hideMapLayerSettings | boolean | false | Hides the map layer settings entry in the map settings UI. |
| mapType | MapType | Map.TYPE_DEFAULT | Base map type to render. |
| mapLayer | MapLayer[] | [] | Active overlay layers such as traffic or incidents. |
| showCluster | boolean | true | Controls whether marker clustering is enabled. |
| showScaleBar | boolean | false | Shows the HERE scale bar. |
| onMapTypeChange | (type: MapType, previousType?: MapType) => void | — | Callback fired when the map type changes. |
| onMapLayerChange | (activeLayer: MapLayer[], previousLayer?: MapLayer[]) => void | — | Callback fired when the active map layers change. |
| onShowClusterChange | (show: boolean) => void | — | Callback fired when the cluster visibility changes. |
| onZoomIn | (zoom: number) => void | — | Callback fired when zooming in via the built-in zoom controls. |
| onZoomOut | (zoom: number) => void | — | Callback fired when zooming out via the built-in zoom controls. |
| onIncidentsChange | (incidentsResult: MapIncidents) => void | — | Callback used by incidents overlays to report fetched incidents. |
| mapSettingsTooltip | string \| React.ReactNode | — | Tooltip content for the map settings entry. |
| mapSettings | React.ReactNode | — | Custom settings UI rendered on top of the map. |
| zoomAnimation | boolean | false | Enables animation when zoom or center changes are applied programmatically. |
| maxViewportHistoryEntries | number | 5 | Maximum number of previous map viewports stored for viewport history. |
| enableWebGL | boolean | true | Enables the WebGL/HARP rendering engine where supported. |
| enableDevicePixelRatio | boolean | true | Uses window.devicePixelRatio for map rendering. |
| enableMarkerIconCache | boolean | true | Enables caching for generated marker icons. |
| enableFractionalZoom | boolean | false | Keeps HERE fractional zoom enabled for smoother trackpad and wheel zoom interaction. |
| disableMarkerPointerEventsWhileZooming | boolean | false | Temporarily disables marker pointer events during zoom interactions to reduce DOM hit-testing overhead. |
| pixelRatio | number | — | Fallback pixel ratio used when device pixel ratio support is disabled. |
| minZoom | number | — | Minimum allowed zoom level. |
| maxZoom | number | — | Maximum allowed zoom level. |
| children | React.ReactNode \| ((api: MapApi, incidents?: MapIncidents) => React.ReactNode) | — | Map content or a render function receiving the map API. |

#### Props: MapPreviousViewportButton

### MapPreviousViewportButton

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| onGoBack | () => void | — | Callback fired after the map viewport history jumps back to the previous viewport. |

#### Props: MapSettingsTile

### MapSettingsTile

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| onToggle | (newState: boolean) => void | — | Callback fired when the settings tile opens or closes. |
| tooltip | React.ReactNode | — | Tooltip shown while the settings tile is closed. |
| panel | React.ReactNode | — | Custom panel rendered below the settings tile when it is open. |
| items | JSX.Element[] \| undefined | — | Dropdown menu items rendered when the settings tile is open. |
| activeIcon | JSX.Element \| undefined | — | Icon shown when the tile is active. |
| inactiveIcon | JSX.Element \| undefined | — | Icon shown when the tile is inactive. |
| isActive | boolean | — | Controls whether the active or inactive icon is shown. |
| buttonClassName | string | — | Additional class name for the clickable settings button. |
| className | string | — | Additional class name for the settings tile wrapper. |