Skip to content

useMapstedMap() — Vue 3 composable

This recipe provides a reusable useMapstedMap() composable that encapsulates the init / destroy lifecycle and exposes the MapInstance handle to your components.

For the full tutorial covering Nuxt 3 SSR, <ClientOnly>, and the .client.vue naming convention, see Embed with Vue 3 and Nuxt.

The composable

ts
// src/composables/useMapstedMap.ts
import { ref, onMounted, onUnmounted } from 'vue';
import { init } from '@mapsted/maps-js-api';
import type { MapInstance } from '@mapsted/maps-js-api';

interface UseMapstedMapOptions {
  propertyId: number;
  mapsDomain?: string;
  onReady?: () => void;
}

export function useMapstedMap(options: UseMapstedMapOptions) {
  const {
    propertyId,
    mapsDomain = 'https://maps.mapsted.com',
    onReady,
  } = options;

  const containerRef = ref<HTMLDivElement | null>(null);
  const instance = ref<MapInstance | null>(null);
  const ready = ref(false);

  onMounted(async () => {
    if (!containerRef.value) return;

    instance.value = await init({
      element: containerRef.value,
      propertyId,
      mapsDomain,
      onload: () => {
        ready.value = true;
        onReady?.();
      },
    });
  });

  onUnmounted(async () => {
    await instance.value?.destroy();
    instance.value = null;
    ready.value = false;
  });

  return { containerRef, instance, ready };
}

Usage

vue
<!-- src/components/MapView.vue -->
<script setup lang="ts">
import { useMapstedMap } from '../composables/useMapstedMap';
import { navigateToFloorById } from '@mapsted/maps-js-api';

const { containerRef, instance, ready } = useMapstedMap({
  propertyId: 1643,
  onReady: () => console.log('Map is ready'),
});

function handleNavigate() {
  if (!ready.value) return;
  navigateToFloorById(42);
}
</script>

<template>
  <div>
    <div ref="containerRef" style="width: 100%; height: 100vh;" />
    <button v-if="ready" @click="handleNavigate">Go to floor</button>
  </div>
</template>

Notes

  • containerRef binding: Pass containerRef to the ref attribute on the container <div>. Vue resolves the template ref automatically because the composable returns a Ref<HTMLDivElement | null>.
  • Reactive ready: ready is a Ref<boolean> — use ready.value in <script setup> logic and ready (no .value) in templates.
  • Nuxt SSR: Wrap the component using <ClientOnly> or name the file .client.vue to prevent onMounted from being called on the server.