Join us online - 19th of November
Save your spot
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 &amp; 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>
    `,
  });
});

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.

How to add annotations to a splat model

Add annotations

Examples

Add interactive labelled pins and popups to a splat model.

Customize annotation pins and colors

Pin styles and categories

Examples

Style annotations with colors, icons, numbers, and custom popup placement.

Position annotations relative to model or geography

Model vs geographic positioning

Examples

Place annotations using model-relative bounding box coordinates or geographic coordinates.

Edit model in custom editor

Edit model in custom editor

Examples

Build a custom 3D editor to adjust the position, rotation, and scale of splat models and save their properties to a database.

Was this helpful?