Get started

Examples

Two React recipes that combine the iframe configuration from the parameters page with the runtime postMessage API. Each is self-contained: drop it onto a page and replace the publishable key and ids. The events page carries the same calls in plain TypeScript.

Switch the displayed itinerary

Put a list of itineraries beside the map and swap the map's content on click, without reloading the iframe. Post a mountaya:setItinerary command to the iframe's contentWindow on each selection. Enable the controls only after the mountaya:ready event, so the first command is never posted too early.

import { useEffect, useRef, useState } from "react";

const ORIGIN = "https://app.mountaya.com";

const ITINERARIES = [
  { id: "itinerary_alpha", label: "Alpine traverse" },
  { id: "itinerary_beta", label: "Lake loop" },
];

export function ItineraryPicker() {
  const ref = useRef<HTMLIFrameElement>(null);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const handler = (event: MessageEvent) => {
      if (event.origin !== ORIGIN || event.data?.type !== "mountaya:ready") {
        return;
      }

      setReady(true);
    };

    window.addEventListener("message", handler);

    return () => window.removeEventListener("message", handler);
  }, []);

  const select = (itineraryId: string) => {
    ref.current?.contentWindow?.postMessage(
      { type: "mountaya:setItinerary", itineraryId },
      ORIGIN,
    );
  };

  return (
    <div>
      <nav>
        {ITINERARIES.map((it) => (
          <button key={it.id} type="button" disabled={!ready} onClick={() => select(it.id)}>
            {it.label}
          </button>
        ))}
      </nav>
      <iframe
        ref={ref}
        src="https://app.mountaya.com/studio/embed?publishable_key=pk_your_publishable_key&terrain=topo"
        title="Mountaya map"
        width="100%"
        height={520}
      />
    </div>
  );
}

The same command carries display overrides, so you can switch the terrain or the visible overlays as the content changes. The events page lists every override.

React to the ready event's bounds

The mountaya:ready event reports the framed itinerary's own bounds as [west, south, east, north]. Drive your own layout from them: render a caption with the extent, fit a static minimap, or note the active content id.

import { useEffect, useState } from "react";

const ORIGIN = "https://app.mountaya.com";

interface ReadyState {
  bounds: [number, number, number, number] | null;
  itineraryId: string | null;
  terrain: string;
}

export function MapWithReadout() {
  const [state, setState] = useState<ReadyState | null>(null);
  const [failed, setFailed] = useState<string | null>(null);

  useEffect(() => {
    const handler = (event: MessageEvent) => {
      if (event.origin !== ORIGIN || event.data?.type !== "mountaya:ready") {
        return;
      }

      if (event.data.error) {
        setFailed(event.data.error);
        return;
      }

      setState({
        bounds: event.data.bounds,
        itineraryId: event.data.itineraryId,
        terrain: event.data.terrain,
      });
    };

    window.addEventListener("message", handler);

    return () => window.removeEventListener("message", handler);
  }, []);

  return (
    <div>
      <iframe
        src="https://app.mountaya.com/studio/embed?publishable_key=pk_your_publishable_key&itinerary_id=itinerary_alpha"
        title="Mountaya map"
        width="100%"
        height={520}
      />
      {failed ? <p>Map failed to load: {failed}</p> : null}
      {state ? (
        <p>
          {state.terrain}, {state.itineraryId ?? "no itinerary"},{" "}
          {state.bounds ? state.bounds.join(", ") : "bounds unavailable"}
        </p>
      ) : (
        <p>Loading map…</p>
      )}
    </div>
  );
}

Next steps