Open Source Package

jamit-maplibre-viewport

A lightweight and fully typed viewport and safe-area manager for MapLibre GL JS applications with dynamic UI overlays.

Measure real interface geometry, keep viewport calculations synchronized with changing layouts and fit map content into the area users can actually see.

Overview

MapLibre camera handling that understands your interface

MapLibre GL JS provides powerful camera APIs, but it does not know which parts of the map are covered by application UI. Sidebars, bottom sheets and floating detail panels can therefore hide markers or important map content even when the camera technically fits the map container.

jamit-maplibre-viewport connects DOM layout geometry with MapLibre camera operations. Registered overlays are measured automatically, layout changes are observed and camera helpers can work with the map area that remains genuinely visible to the user.

Capabilities

Core features

Viewport-aware geometry and camera helpers for MapLibre applications with dynamic interface overlays.

Dynamic overlay measurement

Register sidebars, bottom panels and other interface elements and let the package measure their real DOM geometry instead of relying on hard-coded dimensions.

Automatic layout tracking

ResizeObserver keeps viewport geometry synchronized when registered overlays or the map container change size.

Obstacle-aware coordinate fitting

fitCoordinates() searches for a camera position and zoom level where all supplied coordinates remain visible outside registered UI obstacles.

Real usable map space

Partial overlays only block the area they actually occupy. Free space beside a panel can remain available for map content.

Safe-area calculation

Read the currently available viewport padding and safe area through a fully typed API.

MapLibre camera helpers

Use viewport-aware fitBounds(), fitCoordinates(), flyTo() and easeTo() operations without manually synchronizing UI dimensions with MapLibre.

Consumer safety padding

Add configurable spacing around the map edges and registered obstacles so markers and other important content do not sit directly against interface elements.

Framework independent

The package works directly with MapLibre and standard DOM APIs and does not require Vue, Nuxt, React or another frontend framework.

SSR-safe architecture

Import the package safely in SSR environments. Browser globals are only accessed when actual map and overlay elements are used.

Interactive example

Try the viewport manager

Resize and toggle interface overlays and see how the available map area changes. Fit multiple locations into the space that remains visible to the user.

Dynamic overlayBottom Panel

Resize this panel and run Fit Coordinates again.

Edgebottom
Current height0 px
LIVE MAPLIBRE VIEWPORT DEMO

Toggle the sidebar, resize the bottom panel and compare MapLibre camera operations while the viewport manager keeps track of the real interface geometry.

How it works

DOM geometry meets MapLibre camera logic

The viewport manager connects real interface geometry with camera calculations in five focused steps.

Register overlays

Connect the DOM elements that cover your map.

Measure geometry

The viewport manager measures their actual position and dimensions relative to the map container.

Track changes

ResizeObserver detects dynamic layout changes without polling.

Calculate available space

Overlay rectangles, safe areas and additional consumer padding are converted into usable viewport geometry.

Move the camera

Camera helpers use the calculated geometry to keep important map content visible.

Getting started

Installation

Install jamit-maplibre-viewport together with MapLibre GL JS.

pnpm

Shell
pnpm add jamit-maplibre-viewport maplibre-gl

npm

Shell
npm install jamit-maplibre-viewport maplibre-gl

MapLibre GL JS is provided as a peer dependency.

Supported: MapLibre GL JS >= 5.6.0 < 7

Usage

Create a viewport manager

Create the MapLibre map as usual and pass the map instance to createMapLibreViewport().

TypeScript
import { Map } from 'maplibre-gl';
import { createMapLibreViewport } from 'jamit-maplibre-viewport';

const map = new Map({
  container: 'map',
  style: 'https://demotiles.maplibre.org/style.json',
  center: [13.405, 52.52],
  zoom: 11,
});

const viewport = createMapLibreViewport(map);
Interface overlays

Connect your interface

Register the UI elements that cover parts of the map. The package measures their real DOM rectangles and keeps their geometry synchronized.

TypeScript
viewport.addOverlay({
  id: 'sidebar',
  element: sidebar,
  edge: 'left',
});

viewport.addOverlay({
  id: 'bottom-panel',
  element: bottomPanel,
  edge: 'bottom',
});
Obstacle-aware fitting

Fit locations into the space users can actually see

fitCoordinates() uses the actual registered obstacle rectangles instead of reducing the complete map to simple rectangular edge padding.

The solver searches for the highest fitting zoom level and a valid camera center while keeping the supplied coordinates outside registered overlays.

TypeScript
viewport.fitCoordinates(
  [
    [13.405, 52.52],
    [13.35, 52.5],
    [13.46, 52.54],
  ],
  {
    padding: 40,
    maxZoom: 14,
    duration: 800,
  },
);
Camera API

Familiar MapLibre operations

Use familiar camera operations while the viewport manager provides geometry-aware padding and positioning.

fitBounds()

Fit bounds with a rectangular safe area

fitBounds() uses a rectangular edge-safe area and delegates the final operation to MapLibre.

TypeScript
viewport.fitBounds(bounds, {
  padding: 40,
  maxZoom: 14,
  duration: 800,
});
flyTo()

Fly to a viewport-aware position

Use MapLibre-style flyTo() options while applying the current viewport geometry.

TypeScript
viewport.flyTo({
  center: [13.405, 52.52],
  zoom: 14,
  padding: 40,
  duration: 1200,
});
easeTo()

Ease the camera into place

Move the camera smoothly while respecting the currently calculated viewport padding.

TypeScript
viewport.easeTo({
  center: [13.46, 52.54],
  zoom: 13,
  padding: 40,
  duration: 800,
});
Viewport geometry

Read the available map area

Access the currently calculated viewport padding and rectangular safe area through the typed geometry API.

getPadding()

TypeScript
const padding = viewport.getPadding();
TypeScript
{
  top: 0,
  right: 0,
  bottom: 180,
  left: 320,
}

getSafeArea()

TypeScript
const safeArea = viewport.getSafeArea();
TypeScript
{
  x: 320,
  y: 0,
  width: 960,
  height: 540,
}
Dynamic layouts

Automatically synchronized with your UI

Registered overlays and the map container are monitored with ResizeObserver. When their dimensions change, the internal viewport geometry is invalidated and recalculated before the next camera operation.

This makes collapsing sidebars, responsive panels and expanding bottom sheets possible without maintaining hard-coded layout dimensions in the map logic.

Manual refresh

Refresh when you need immediate control

refresh() forces an immediate geometry recalculation. It does not automatically move the map camera.

TypeScript
viewport.refresh();

viewport.fitCoordinates(coordinates, {
  padding: 40,
});
Lifecycle

Clean lifecycle

Destroying the viewport manager disconnects observers, cancels scheduled animation frames and releases internal references.

TypeScript
viewport.destroy();
Framework independent

Use it wherever MapLibre runs

jamit-maplibre-viewport depends on MapLibre and standard browser APIs, not on a UI framework.

  • Vanilla TypeScript
  • Vue
  • Nuxt
  • React
  • Next.js
  • Svelte
SSR

Safe to import on the server

The package can be imported in server-side rendered applications without requiring window, document, HTMLElement or ResizeObserver during module evaluation.

DOM access starts only when a viewport manager interacts with an actual MapLibre instance and registered elements.

Compatibility

Built for modern MapLibre applications

The package has been validated against MapLibre GL JS 5.6 and 6.0 using type checks, automated tests and library builds.

  • MapLibre GL JS 5.6
  • MapLibre GL JS 6.0
  • TypeScript
  • ResizeObserver
Current limitations

Defined behavior for unsupported camera cases

Unsupported scenarios are rejected explicitly to prevent incorrect or misleading camera results.

Bearing and pitch

fitCoordinates() currently supports only maps with bearing 0 and pitch 0. Rotated or tilted maps intentionally produce a clear error instead of an incorrect camera result.

Text
bearing = 0
pitch = 0

Antimeridian

Coordinate groups that cross the international date line are currently rejected intentionally. Full world-wrapping support is planned for a future version.

TypeScript
[
  [179, 10],
  [-179, 10],
]
Project status

Production-ready viewport management for MapLibre

Dynamic overlay measurement, ResizeObserver-based layout tracking, safe-area calculation, viewport-aware camera helpers and obstacle-aware coordinate fitting are fully implemented and covered by automated tests.

The package is publicly available on npm and includes support for MapLibre GL JS 5.6 and 6.0, TypeScript definitions, SSR-safe imports, automated CI checks and a live interactive demo.

Production ready
Open source

Explore the source code

The complete source code, automated tests, MapLibre demo and release history are available in the public GitHub repository.

Open GitHub repository