Skip to main content

Multiview Map

Last updated: April 29, 2026 | 5 minutes read

In this guide, you will learn how to display multiple interactive maps in one viewport.

Live Demo​

Overview​

This example demonstrates the following features:

  • Display multiple map views in a grid layout
  • Each map view is independently interactive (panning, zooming)
  • Dynamic add/remove map views with buttons
  • Maximum of 4 map views in a 2x2 grid

Code Implementation​

Setup UI and Grid​

import {
GemKit,
GemMap,
PositionService,
} from '@magiclane/maps-sdk';
import { GEMKIT_TOKEN } from './token';
const MAX_VIEWS = 4;
let mapViewsCount = 0;
type MapViewEntry = { container: HTMLElement, gemMap: GemMap | null };
const mapViews: MapViewEntry[] = [];
let gemKit: any = null;

// UI setup
function setupUI() {
// App bar
const appBar = document.createElement('div');
appBar.style.cssText = `
width: 100% !important; height: 56px !important; background: #4527a0; color: #fff;
display: flex; align-items: center; justify-content: space-between;
padding: 0 16px; font-size: 1.2em; font-weight: 500; position: fixed; top: 0; left: 0; z-index: 2000;
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
`;
appBar.innerHTML = `
<span>Multiview Map</span>
<span>
<button id="addViewBtn" style="background:none;border:none;color:#fff;font-size:1.5em;cursor:pointer;margin-right:8px;">➕</button>
<button id="removeViewBtn" style="background:none;border:none;color:#fff;font-size:1.5em;cursor:pointer;">➖</button>
</span>
`;
document.body.appendChild(appBar);

// Button handlers
(document.getElementById('addViewBtn') as HTMLButtonElement).onclick = addViewButtonPressed;
(document.getElementById('removeViewBtn') as HTMLButtonElement).onclick = removeViewButtonPressed;

// Grid container
const grid = document.createElement('div');
grid.id = 'map-grid';
grid.style.cssText = `
position: absolute; top: 56px; left: 0; width: 100vw; height: calc(100vh - 56px);
display: grid; grid-template-columns: 1fr 1fr; grid-auto-rows: 1fr; gap: 10px; padding: 10px;
background: #f5f5f5;
`;
document.body.appendChild(grid);
}

Add and Remove Map Views​

// Add a new map view
function addViewButtonPressed() {
if (mapViewsCount >= MAX_VIEWS) return;
mapViewsCount += 1;
renderMapViews();
}

// Remove the last map view
function removeViewButtonPressed() {
if (mapViewsCount <= 0) return;
mapViewsCount -= 1;
renderMapViews();
}

Render Map Views​

// Render the map views in the grid
function renderMapViews() {
const grid = document.getElementById('map-grid');
if (!grid) return;

// Add new map views if needed
while (mapViews.length < mapViewsCount) {
const i = mapViews.length;
const mapContainer = document.createElement('div');
mapContainer.style.cssText = `
border: 1px solid #222; border-radius: 10px; background: #fff; min-width: 0; min-height: 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
overflow: hidden; display: flex; align-items: stretch; justify-content: stretch;
`;
mapContainer.id = `map-view-${i}`;
let entry: MapViewEntry = { container: mapContainer, gemMap: null };
// Create a new map view only once
const wrapper = gemKit.createView(i, (gemMap: GemMap) => {
entry.gemMap = gemMap;
});
if (wrapper) mapContainer.appendChild(wrapper);
grid.appendChild(mapContainer);
mapViews.push(entry);
}

// Remove extra map views if needed
while (mapViews.length > mapViewsCount) {
const entry = mapViews.pop();
if (entry) {
// Some GemKit SDKs do not declare release() in GemMap typings, but it exists at runtime
if (entry.gemMap && typeof (entry.gemMap as any).release === 'function') {
(entry.gemMap as any).release();
}
if (entry.container && entry.container.parentNode) {
entry.container.parentNode.removeChild(entry.container);
}
}
}
}

Initialize Application​

// Main entry
window.addEventListener('DOMContentLoaded', async () => {
setupUI();

gemKit = await GemKit.initialize(GEMKIT_TOKEN);
await PositionService.instance;

// Start with 0 views (or set to 1 if you want a default)
mapViewsCount = 0;
renderMapViews();
});

Key Features​

Grid Layout​

The example uses CSS Grid for responsive layout:

  • 2 columns: Maps displayed in a 2x2 grid
  • Auto rows: Each row takes equal height
  • Gap and padding: 10px spacing between map views
  • Responsive: Adjusts to viewport size

Dynamic View Management​

Map views are managed dynamically:

  • Add views: Click ➕ to add a new map (up to 4)
  • Remove views: Click ➖ to remove the last map
  • Independent maps: Each map is a separate instance
  • Proper cleanup: Map resources are released when removed

View Creation​

Each map view is created with:

  • Unique view ID (0, 1, 2, 3)
  • Individual container element
  • Separate GemMap instance
  • Independent interaction (pan, zoom)

Implementation Details​

Map View Entry Type​

type MapViewEntry = {
container: HTMLElement,
gemMap: GemMap | null
};

This tracks both the DOM container and the GemMap instance for each view.

Resource Management​

When removing a map view:

  1. Pop the entry from the array
  2. Release the GemMap instance (if available)
  3. Remove the container from the DOM
  4. Clean up resources properly

View Limits​

The maximum number of views is set to 4:

  • Prevents performance issues
  • Maintains usable view size
  • Fits in 2x2 grid layout

Use Cases​

  • Comparison: Compare different map locations side by side
  • Multi-region monitoring: View multiple areas simultaneously
  • Before/after: Compare different map styles or time periods
  • Analysis: Analyze different map layers or data sets

Next Steps​