For AI agents: a documentation index is available at /llms.txt. Markdown versions of all pages are available by appending .md to any URL.
Create rich HTML popups in annotations
This example demonstrates how to create rich HTML popups on 3D Gaussian splat annotations, including custom card widths, cover imagery, structured specification tables, and external action links.
NPM module setup
npm install --save @maptiler/geosplats
import { SplatModel, Map, config } from "@maptiler/geosplats";
import "@maptiler/geosplats/dist/maptiler-geosplats.css";
config.apiKey = "YOUR_MAPTILER_API_KEY_HERE";
const map = new Map({
apiKey: config.apiKey,
container: "map",
center: [-80.27822, 25.74077],
zoom: 17.5,
pitch: 50,
bearing: -15,
});
let splatModel;
map.on("load", () => {
splatModel = new SplatModel({
model: "YOUR_MAPTILER_SPLAT_MODEL_ID_HERE",
revealParams: { enabled: true },
});
map.addSplatModel(splatModel);
});
map.on("splatmodel:load", () => {
// 1. Popup with an image, styled typography, and badge
splatModel.addAnnotation({
pos: { x: -0.4, z: -0.2 },
icon: "ποΈ",
label: "Sun Pool",
popupWidth: 320,
popup: `
<div style="font-family: system-ui, -apple-system, sans-serif; text-align: left;">
<h4 style="margin: 4px 0 8px; font-size: 15px;">ποΈ Sun Pool & Lounge</h4>
<img src="https://images.unsplash.com/photo-1576013551627-0cc20b96c2a7?w=500&auto=format&fit=crop&q=60" style="width: 100%; height: 130px; object-fit: cover; border-radius: 6px; margin-bottom: 8px;" alt="Sun Pool" />
<p style="margin: 0 0 6px; color: #444; font-size: 12.5px; line-height: 1.4;">Resort outdoor pool heated to 28Β°C with private cabanas and cocktail service.</p>
<span style="display: inline-block; padding: 2px 8px; font-size: 11px; font-weight: 600; color: #166534; background: #dcfce7; border-radius: 9999px;">Open until 22:00</span>
</div>
`,
});
// 2. Structured data table
splatModel.addAnnotation({
pos: { x: 0.1, z: 0.2 },
icon: "π",
label: "Underground Parking",
popupWidth: 280,
popup: `
<div style="font-family: system-ui, -apple-system, sans-serif; text-align: left; font-size: 12.5px;">
<h4 style="margin: 4px 0 8px; font-size: 15px;">π Secure Parking</h4>
<table style="width: 100%; border-collapse: collapse; margin-top: 4px;">
<tr style="border-bottom: 1px solid #eee;"><th style="padding: 4px 0; color: #666; font-weight: 500;">Capacity</th><td style="padding: 4px 0; font-weight: 600; text-align: right;">240 bays</td></tr>
<tr style="border-bottom: 1px solid #eee;"><th style="padding: 4px 0; color: #666; font-weight: 500;">Height limit</th><td style="padding: 4px 0; font-weight: 600; text-align: right;">2.1 m</td></tr>
<tr style="border-bottom: 1px solid #eee;"><th style="padding: 4px 0; color: #666; font-weight: 500;">EV chargers</th><td style="padding: 4px 0; font-weight: 600; text-align: right;">12 ports</td></tr>
<tr><th style="padding: 4px 0; color: #666; font-weight: 500;">Guest rate</th><td style="padding: 4px 0; font-weight: 600; text-align: right;">Included</td></tr>
</table>
</div>
`,
});
// 3. Action card with call-to-action link
splatModel.addAnnotation({
pos: { x: 0.6, z: 0.46 },
icon: "πΎ",
label: "Tennis Club",
popupWidth: 320,
popup: `
<div style="font-family: system-ui, -apple-system, sans-serif; text-align: left;">
<h4 style="margin: 4px 0 6px; font-size: 15px;">πΎ Championship Tennis Club</h4>
<p style="margin: 0 0 10px; color: #444; font-size: 12.5px; line-height: 1.4;">10 lighted Har-Tru clay courts with racket rentals, pro shop, and certified trainers.</p>
<a href="https://www.maptiler.com" target="_blank" rel="noopener" style="display: block; text-align: center; padding: 7px 12px; background: #3174ff; color: #fff; text-decoration: none; border-radius: 4px; font-size: 12px; font-weight: 600;">Reserve Court</a>
</div>
`,
});
});
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex" />
<title>Create rich HTML popups in annotations</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="map"></div>
<script type="module" src="./main.js"></script>
</body>
</html>
body {
font-family: Inter, sans-serif;
margin: 0;
overflow: hidden;
}
#map {
width: 100%;
height: 100vh;
}
Customizing popup width
By default, popups render with a width of 300 px (or 240 px on touch and small screens). Use the popupWidth option to set an explicit width in CSS pixels:
splatModel.addAnnotation({
pos: { x: -0.4, z: -0.2 },
icon: "ποΈ",
label: "Sun Pool",
popupWidth: 320,
popup: "<div>...</div>",
});
The maximum height defaults to 400 px (300 px on small screens). Any content taller than this threshold automatically scrolls while keeping the close button accessible.
Embedding cards, images, and tables
Popups accept arbitrary HTML strings. You can style the layout directly with inline styles or through external CSS classes:
- Media cards: Include photos, badges, or illustrations with responsive dimensions (
style="width: 100%; object-fit: cover;"). - Data tables: Structure key specifications (such as parking bay capacity, clearance heights, or pricing) with clean table layouts.
- Action buttons: Provide direct links or triggers for booking engines, ticket sales, or external documentation.
splatModel.addAnnotation({
pos: { x: 0.1, z: 0.2 },
icon: "π",
label: "Parking",
popupWidth: 280,
popup: `
<div style="font-family: system-ui, sans-serif;">
<h4>π Secure Parking</h4>
<table style="width: 100%; border-collapse: collapse;">
<tr><th>Spaces</th><td>240</td></tr>
<tr><th>Clearance</th><td>2.1 m</td></tr>
</table>
</div>
`,
});
Security and sanitization
The HTML string provided to popup is inserted directly into the DOM without automated sanitization. While <script> tags do not execute, inline event handlers (like <img onerror="...">) can be triggered.
Always sanitize any untrusted or user-generated input before passing it into popup.
Learn more
Check out the MapTiler GeoSplats SDK reference for full configuration details on popups and annotations.
Related examples
Pin styles and categories
ExamplesStyle annotations with colors, icons, numbers, and custom popup placement.
Model vs geographic positioning
ExamplesPlace annotations using model-relative bounding box coordinates or geographic coordinates.
Edit model in custom editor
ExamplesBuild a custom 3D editor to adjust the position, rotation, and scale of splat models and save their properties to a database.