Skip to content

Instantly share code, notes, and snippets.

@vgeorge
Created August 27, 2025 16:20
Show Gist options
  • Select an option

  • Save vgeorge/a725b7769cc548b6e19871eaa933015d to your computer and use it in GitHub Desktop.

Select an option

Save vgeorge/a725b7769cc548b6e19871eaa933015d to your computer and use it in GitHub Desktop.
import React, { useEffect } from "react";
import { ReactPlugin } from "../../core/plugin";
/**
* Mapbox iframe integration issue
*
* Problem:
* - Mapbox checks `options.container instanceof window.HTMLElement`
* - In plugin iframes, DOM elements come from a different realm (iframe’s global object)
* - So instanceof fails, even if container is a valid <div>
*
* Root cause:
* - Each iframe has its own global constructors (HTMLElement, Element, etc.)
* - instanceof comparisons fail across realms
*
* Solution:
* - Temporarily monkey patch HTMLElement in this frame
* - Set the container’s prototype to match the patched constructor
* - Restore original constructors after map creation
*/
declare global {
interface Window {
mapboxgl: any;
}
}
// Get Mapbox access token from environment variables
const MAPBOX_ACCESS_TOKEN = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
if (!MAPBOX_ACCESS_TOKEN) {
throw new Error(
"VITE_MAPBOX_ACCESS_TOKEN environment variable is required. Please check your .env file."
);
}
function createMapWithMonkeyPatch(container: HTMLElement) {
// Store original constructors
const originalHTMLElement = window.HTMLElement;
const originalElement = window.Element;
// Create a custom HTMLElement constructor that recognizes our element
const CustomHTMLElement = function () {};
CustomHTMLElement.prototype = originalHTMLElement.prototype;
// Override the instanceof check by changing the element's prototype chain
Object.setPrototypeOf(container, CustomHTMLElement.prototype);
// Temporarily replace global constructors
(window as any).HTMLElement = CustomHTMLElement;
(window as any).Element = CustomHTMLElement;
try {
// Create the map - now instanceof checks should pass
const map = new window.mapboxgl.Map({
container: container,
accessToken: MAPBOX_ACCESS_TOKEN,
style: "mapbox://styles/mapbox/streets-v11",
center: [-100, 40],
zoom: 3.5,
});
return map;
} finally {
// Restore original constructors to avoid side effects
(window as any).HTMLElement = originalHTMLElement;
(window as any).Element = originalElement;
}
}
const MapComponent: React.FC = () => {
const mapRef = React.useRef<any>(null);
const containerRef = React.useRef<HTMLDivElement>(null);
useEffect(() => {
if (containerRef.current && window.mapboxgl) {
try {
const map = createMapWithMonkeyPatch(containerRef.current);
mapRef.current = map;
} catch (error) {
console.error("Error creating Mapbox map:", error);
}
}
return () => {
if (mapRef.current) {
mapRef.current.remove();
mapRef.current = null;
}
};
}, []);
return (
<div
ref={containerRef}
id="map"
style={{ width: "100%", height: "500px" }}
/>
);
};
export default class MapLayerPlugin extends ReactPlugin<{}, {}> {
pluginProperties() {
return {};
}
component(): React.ReactNode {
return <MapComponent />;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment