Nuxt
You will end up with a Nuxt page that ships the first map image in the server HTML and becomes an interactive map in the browser.
Install
Section titled “Install”npm install instantmap-jsTell Vue that instant-map is a custom element.
export default defineNuxtConfig({ compatibilityDate: "2026-11-01", vue: { compilerOptions: { isCustomElement: (tag) => tag === "instant-map" } },});Register the element in the browser with a client plugin.
import { defineInstantMap } from "instantmap-js";
export default defineNuxtPlugin(() => { defineInstantMap();});Add the map
Section titled “Add the map”<script setup lang="ts">import type { InstantMapElement, Marker } from "instantmap-js";import { staticUrl } from "instantmap-js/server";
const center: [number, number] = [-97.7431, 30.2672];const zoom = 12;const listings: Marker[] = [ { id: 1, lng: -97.75, lat: 30.26, label: "$120", title: "Apartment 1", subtitle: "$120 per night" }, { id: 2, lng: -97.73, lat: 30.28, label: "$85", title: "Apartment 2", subtitle: "$85 per night" },];
const poster = staticUrl({ center, zoom, width: 1280, height: 720, ratio: 2, markers: listings });const map = ref<InstantMapElement | null>(null);const clicked = ref<Marker | null>(null);
function onMarkerClick(event: Event) { clicked.value = (event as CustomEvent<{ marker: Marker }>).detail.marker;}
function showAll() { map.value?.fitBounds(listings, { padding: 40 });}</script>
<template> <button type="button" @click="showAll">Show all</button> <span v-if="clicked">Selected: {{ clicked.title }}, {{ clicked.subtitle }}</span> <instant-map ref="map" :center="center.join(',')" :zoom="zoom" :markers.prop="listings" style="display: block; position: relative; height: 520px" @markerclick="onMarkerClick" > <img :src="poster" alt="Map of Austin" width="1280" height="720" fetchpriority="high" style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover" /> </instant-map></template>Render the first image on the server
Section titled “Render the first image on the server”staticUrl runs during server rendering and returns the URL of the first image. The <img> is part of the HTML Nuxt sends, so the map shows before JavaScript loads. The element takes over in the browser.
To check it, build and preview, then look at the HTML the server sends:
npm run buildnpm run previewcurl -s http://localhost:3000/ | grep "<img"You should see an <img src="https://.../static?..."> inside <instant-map>. View source in the browser shows the same.
Markers, popups and events
Section titled “Markers, popups and events”centerandzoomare attributes. Bind them with:centerand:zoom.- Markers are objects, so bind them as a property:
:markers.prop="listings". - Listen with
@markerclick,@moveendor any other event. The payload is inevent.detail. - Call methods through a template ref:
map.value?.fitBounds(...). - Markers set as a property do not change the server image. Pass the same markers to
staticUrl, as above.
Common mistakes
Section titled “Common mistakes”- Registering the element in a plain plugin. Use the
.client.tssuffix, because the element needs the browser. - Missing
isCustomElement. Vue warns it cannot resolveinstant-map. - Wrapping the map in
<ClientOnly>. You lose the server-rendered image, which is the point. - Mismatched poster and element. Use the same
center,zoomand size, or the image jumps on hydration. - No height. Set one on the element.
- Calling
defineInstantMap()from server code. Keep it in the client plugin.instantmap-js/serveris the only entry that is safe there.
Prompt for your agent
Section titled “Prompt for your agent”Add Instant Map to my Nuxt 4 app. Follow https://instantmap.org/docs/frameworks/nuxt.mdSet isCustomElement in nuxt.config.ts, register the element in app/plugins/instantmap.client.ts,render the first image on the server with staticUrl from instantmap-js/server,and bind markers with :markers.prop.Example
Section titled “Example”A full working project is at examples/nuxt.