Preconnecting Map Hosts
A small change in your page bootstrap can shave 150–300 ms off the first map render. The trick
is <link rel="preconnect"> — telling the browser to start the DNS lookup + TLS handshake to the
hosts that MapLibre will hit before it actually needs them.
The SDK ships a small helper that wires this up for you idempotently.
When this helps
When you call sdk.createMap(...), MapLibre fires a cascade of requests to several hosts the moment
the style JSON arrives:
tiles.mapvx.com— sprite (icons used by your style)api.maptiler.com— basemap vector tiles + glyphs (fonts)indoorequals.mapvx.com— indoor tile TileJSON
If none of these have a warm connection, each one pays the TCP + TLS cost (~100–200 ms each) right when the user is staring at a half-rendered map. Preconnecting starts that handshake while the SDK is still downloading the style JSON, so by the time MapLibre needs the resource the socket is ready.
Quick start
The simplest form: call injectPreconnects with the SDK’s default list before initializeSDK.
import { initializeSDK, injectPreconnects, MAPVX_DEFAULT_PRECONNECT_HOSTS } from "@mapvx/web-js"
// Call as early as possible — top of your bootstrap is ideal.
injectPreconnects(MAPVX_DEFAULT_PRECONNECT_HOSTS)
const sdk = initializeSDK(apiKey, { lang: "es" })
const map = sdk.createMap(document.getElementById("map"), {
parentPlaceId,
center,
zoom,
})For UMD / vanilla HTML:
<script src="https://unpkg.com/maplibre-gl@5.7.0/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/@mapvx/web-js/dist/umd/index.js"></script>
<script>
const { initializeSDK, injectPreconnects, MAPVX_DEFAULT_PRECONNECT_HOSTS } = window.MapVX
injectPreconnects(MAPVX_DEFAULT_PRECONNECT_HOSTS)
const sdk = initializeSDK("YOUR_API_KEY")
// …
</script>Custom hosts
If your deployment uses a custom tile/sprite/glyph CDN, pass those instead (or in addition to the defaults):
injectPreconnects([
...MAPVX_DEFAULT_PRECONNECT_HOSTS,
"https://tiles.your-company.com",
"https://fonts.your-company.com",
])Bare hostnames are normalized to https:// for you:
injectPreconnects(["tiles.mapvx.com", "api.maptiler.com"])
// → injects https://tiles.mapvx.com and https://api.maptiler.comBehaviour
- Idempotent. If a host already has a
<link rel="preconnect" crossorigin>for it in<head>, the call is a no-op for that host. Safe to call multiple times — for example on hot module reload during development. - Upgrades partial hints. If your page (or a CDN/SSR template) already emits
<link rel="preconnect" href="…">withoutcrossorigin, the helper addscrossorigin="anonymous"to that existing link instead of leaving it broken. Withoutcrossoriginthe warmed socket cannot be reused for the CORS tile/font/sprite fetches MapLibre performs, so the existing hint would silently no-op. An explicitcrossorigin="use-credentials"is respected and never overwritten. - Adds
dns-prefetchfallback. Older browsers that ignorepreconnectstill benefit from the DNS hint. - Returns the origins where the helper made a change — either added a new link or upgraded an existing one. Skipped hosts (already configured, invalid input) are omitted.
- SSR-safe. Returns an empty array when
documentis not defined.
Pure HTML alternative
If you’d rather not depend on the helper, the equivalent is two <link> tags per host in your
<head>:
<link rel="preconnect" href="https://tiles.mapvx.com" crossorigin />
<link rel="dns-prefetch" href="https://tiles.mapvx.com" />
<link rel="preconnect" href="https://api.maptiler.com" crossorigin />
<link rel="dns-prefetch" href="https://api.maptiler.com" />
<link rel="preconnect" href="https://indoorequals.mapvx.com" crossorigin />
<link rel="dns-prefetch" href="https://indoorequals.mapvx.com" />The crossorigin attribute is required — without it the connection cannot be reused for the actual
tile / font / sprite fetches (which CORS).
Measuring the impact
Compare the time between style.load and load events on the MapLibre map before and after
enabling preconnects. The shorter that window, the smoother the first paint feels — that’s the gap
during which the basemap appears without sprite icons, glyphs, or indoor data.