Skip to content

Embedding with React / Vue

Integrate Layota maps into your frontend framework using the JavaScript SDK.

React

Basic Component

tsx
import { useEffect, useRef } from 'react'
import { LayotaMap } from '@layota/map-sdk'

export function MapView({ projectId }: { projectId: string }) {
  const containerRef = useRef<HTMLDivElement>(null)
  const mapRef = useRef<LayotaMap | null>(null)

  useEffect(() => {
    if (!containerRef.current) return

    const map = new LayotaMap({
      container: containerRef.current,
      projectId,
    })

    mapRef.current = map

    map.ready().then(() => {
      console.log('Map is ready')
    })

    return () => {
      map.destroy()
    }
  }, [projectId])

  return <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
}

With Events

tsx
import { useEffect, useRef, useCallback } from 'react'
import { LayotaMap, Area } from '@layota/map-sdk'

interface Props {
  projectId: string
  onAreaClick?: (area: Area) => void
}

export function MapView({ projectId, onAreaClick }: Props) {
  const containerRef = useRef<HTMLDivElement>(null)
  const mapRef = useRef<LayotaMap | null>(null)

  useEffect(() => {
    if (!containerRef.current) return

    const map = new LayotaMap({
      container: containerRef.current,
      projectId,
    })

    mapRef.current = map

    map.on('areaClick', (area) => {
      onAreaClick?.(area)
    })

    return () => map.destroy()
  }, [projectId])

  const selectArea = useCallback((areaId: string) => {
    mapRef.current?.selectArea(areaId).focusOn(areaId)
  }, [])

  return <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
}

Usage

tsx
function App() {
  return (
    <MapView
      projectId="your-project-id"
      onAreaClick={(area) => console.log('Clicked:', area.title)}
    />
  )
}

Vue 3

Basic Component

vue
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { LayotaMap } from '@layota/map-sdk'

const props = defineProps<{
  projectId: string
}>()

const emit = defineEmits<{
  areaClick: [area: { id: string; title: string }]
}>()

const container = ref<HTMLElement>()
let map: LayotaMap | null = null

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

  map = new LayotaMap({
    container: container.value,
    projectId: props.projectId,
  })

  map.on('areaClick', (area) => {
    emit('areaClick', area)
  })

  await map.ready()
})

onUnmounted(() => {
  map?.destroy()
})

function selectArea(areaId: string) {
  map?.selectArea(areaId).focusOn(areaId)
}

defineExpose({ selectArea })
</script>

<template>
  <div ref="container" style="width: 100%; height: 600px" />
</template>

Usage

vue
<script setup lang="ts">
import MapView from './MapView.vue'

function handleAreaClick(area: { id: string; title: string }) {
  console.log('Clicked:', area.title)
}
</script>

<template>
  <MapView
    project-id="your-project-id"
    @area-click="handleAreaClick"
  />
</template>

Key Points

  • Always call destroy() when the component unmounts to clean up the iframe and event listeners
  • One map per container — don't reuse the same DOM element for multiple maps
  • ready() is optional — you can subscribe to events before the map loads; they'll fire when ready
  • The SDK handles the iframe lifecycle — you just need a container element

Layota Documentation