Skip to content

Map integration ​

Embed the interactive floor plan on your website by importing load from https://unpkg.com/@expofp/floorplan and pointing it at your event’s manifest URL (see also load() in the API reference).

💡 Make sure the container has a set width and height

Method 1: Initialize with JavaScript ​

  • Add a container element to hold the floor plan.
  • Load the SDK from CDN in a <script type="module"> block, for example import { load } from 'https://unpkg.com/@expofp/floorplan'.
  • Call load with a manifest reference ($ref to your event’s manifest.json) and options (for example element).

This method gives you direct control over initialization in your application’s JavaScript.

📝 Other options are available — see the API reference.

Add a container with id="floorplan" (or pass another element in options), then use a module script:

js
import { load } from 'https://unpkg.com/@expofp/floorplan';
load(
  { $ref: 'https://demo.expofp.com/manifest.json' },
  { element: document.querySelector('#floorplan') },
);

Open live example

Single-page apps (React, Vue, Svelte…) ​

When the plan lives on a route that mounts and unmounts, call destroy() on the way out. It unmounts the UI, releases the listeners, analytics and map resources, and empties the container so the same element can host a later load(). It is also safe to call while the plan is still loading — that cancels the load rather than throwing.

One page can host only one plan at a time — a load() that starts while the previous plan is still alive rejects with A plan is already loaded on this page; call destroy() first (see Switching plans). destroy() is synchronous, but the plan you call it on only exists once load() has resolved, so an unmount that fires mid-load has nothing to tear down yet. Chaining both through one promise keeps them in order. This matters in development, where React's StrictMode mounts every effect twice:

jsx
// Module scope, not a ref: it serializes load → destroy → load across every
// mount of the route, including two that overlap on a fast navigation.
let chain = Promise.resolve();

function Map() {
  useEffect(() => {
    const element = document.getElementById('floorplan');
    let plan;

    chain = chain
      .then(async () => {
        plan = await load({ $ref: MANIFEST }, { element });
      })
      // Every stage ends in a catch: one rejected load would otherwise leave
      // `chain` permanently rejected, and no later mount or teardown on it
      // would ever run.
      .catch((error) => console.error('floor plan failed to load', error));

    return () => {
      chain = chain
        .then(() => plan?.destroy())
        .catch((error) => console.error('floor plan failed to tear down', error));
    };
  }, []);

  return <div id="floorplan" style={{ height: 600, width: '100%' }} />;
}

If you also await ready, catch it as well: destroying a plan mid-load rejects that promise, because the initialization it was waiting on has been cancelled.

Open live example

Method 2: Using an iframe ​

To embed the floor plan, insert an iframe with your event URL.

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Iframe Integration — ExpoFP SDK Example</title>
  </head>
  <body
    style="margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center"
  >
    <iframe
      style="width: 90%; height: 90%; border: 0"
      src="https://demo.expofp.com?consent=denied"
      allow="clipboard-read; clipboard-write"
    ></iframe>
  </body>
</html>

Open live example

Switching plans ​

One live plan per page. To show a different event on the same page — a client-side route change on your site, for example — end the current plan and then load the next one:

js
plan.destroy();
plan = await load(
  { $ref: 'https://another-event.expofp.com/manifest.json' },
  { element: document.querySelector('#floorplan') },
);

The second plan is built from scratch: its own booths, layers, styling and page title, with nothing carried over from the event you left. You can reuse the same container element, or point at a different one. Loading the same event again is a genuine fresh load, so “back” navigation behaves like a first visit.

destroy() is synchronous and safe to call twice, so calling it from a framework unmount hook needs no guard:

js
useEffect(() => () => plan?.destroy(), []);

Destroy before you load

Calling load() while a plan is still alive rejects immediately:

A plan is already loaded on this page; call destroy() first (one live plan per page — see the Switching plans guide).

Nothing is fetched or rendered, and the plan you already have keeps working.

Showing two plans at once ​

Two plans cannot share one document. To show several events side by side, give each one its own iframe (see Method 2) — the browser keeps them isolated for you.