DocsThe app hook

The app hook

Register the map with one line in test and development builds; nothing is imported and nothing ships.

map.tsTypeScript
const map = new maplibregl.Map({ ... });(window.__geowright_maps ??= {}).main = map;// that's it; nothing is imported, nothing ships

Keep that line out of production builds (if (import.meta.env.MODE !== 'production'), or your bundler's equivalent): it makes the map instance reachable by every script on the page (SECURITY.md).

In TypeScript, add /// <reference types="geowright/app" /> to a .d.ts file your app's tsconfig includes (a Vite app has src/vite-env.d.ts) and the line compiles as written, without (window as any). It is types only: nothing ships.

With a wrapper the map instance arrives in a callback — still one line (all three verified in wild/apps/):

Map componentsJSX
<Map onLoad={e => ((window.__geowright_maps ??= {}).main = e.target)} … />// react-map-gl<MapLibre onload={(e) => ((window.__geowright_maps ??= {}).main = e.target)} … />// svelte-maplibre-gl<mgl-map @map:load="e => ((e.map.getContainer().ownerDocument.defaultView.__geowright_maps ??= {}).main = e.map)" … />// ^ vue-maplibre-gl: a Vue template cannot see `window`; in <script setup> the plain line works

Wrappers fire that callback on MapLibre's load, which needs every declared source: if a data request fails, the map is never registered.

The in-page adapter is injected by Playwright at test time. (Auto-discovery is not possible for bundled ESM apps — tests/discovery.spec.ts proves it.)

If the map is never found or never becomes idle, see Troubleshooting.