Api

<ScriptGoogleMapsOverlayView>

Renders arbitrary Vue slot content at a map lat/lng position. Unlike InfoWindow, you have full control over HTML structure and styling.

positiongoogle.maps.LatLngLiteral

Geographic position for the overlay. Falls back to parent marker position if omitted.

anchorOverlayAnchor = 'bottom-center'

Anchor point of the overlay relative to its position.

offset{ x: number, y: number }

Pixel offset from the anchor position.

paneOverlayPane = 'floatPane'

The map pane on which to render the overlay.

zIndexnumber

CSS z-index for the overlay element.

blockMapInteractionboolean = true

Whether to block map click and gesture events from passing through the overlay.

panOnOpenboolean | number = true

Pan the map so the overlay is fully visible when opened, similar to InfoWindow behavior. Set to `true` for default 40px padding, or a number for custom padding.

hideWhenClusteredboolean = true

Automatically hide the overlay when its parent marker joins a cluster (on zoom out). Only applies when nested inside a ScriptGoogleMapsMarkerClusterer.

v-model:openboolean

Marker Anchoring

When nested inside a ScriptGoogleMapsMarker, the overlay automatically inherits the marker's position. This makes it a fully customizable alternative to InfoWindow, the overlay follows the marker when dragged.

<template>
  <ScriptGoogleMaps api-key="your-api-key">
    <ScriptGoogleMapsMarker
      :position="{ lat: -34.397, lng: 150.644 }"
      :options="{ gmpDraggable: true }"
    >
      <ScriptGoogleMapsOverlayView
        anchor="bottom-center"
        :offset="{ x: 0, y: -50 }"
      >
        <div class="custom-tooltip">
          Fully custom content, no InfoWindow constraints
        </div>
      </ScriptGoogleMapsOverlayView>
    </ScriptGoogleMapsMarker>
  </ScriptGoogleMaps>
</template>

Using v-model:open keeps the overlay mounted, toggling visibility via CSS. This avoids remount cost and preserves internal state.

<script setup lang="ts">
const open = ref(false)
</script>

<template>
  <ScriptGoogleMaps api-key="your-api-key">
    <ScriptGoogleMapsMarker
      :position="{ lat: -34.397, lng: 150.644 }"
      @click="open = !open"
    >
      <ScriptGoogleMapsOverlayView
        v-model:open="open"
        anchor="bottom-center"
        :offset="{ x: 0, y: -50 }"
      >
        <div class="custom-popup">
          <button @click.stop="open = false">
            ×
          </button>
          <p>Any Vue content here</p>
        </div>
      </ScriptGoogleMapsOverlayView>
    </ScriptGoogleMapsMarker>
  </ScriptGoogleMaps>
</template>

For simple cases where remounting is acceptable, v-if also works:

<ScriptGoogleMapsMarker
  :position="{ lat: -34.397, lng: 150.644 }"
  @click="open = true"
>
  <ScriptGoogleMapsOverlayView v-if="open">
    <MyPopup @close="open = false" />
  </ScriptGoogleMapsOverlayView>
</ScriptGoogleMapsMarker>

Persistent Label

<template>
  <ScriptGoogleMaps api-key="your-api-key">
    <ScriptGoogleMapsOverlayView
      :position="{ lat: -34.397, lng: 150.644 }"
      anchor="center"
      :block-map-interaction="false"
    >
      <span class="bg-white px-1.5 py-0.5 rounded">
        Label text
      </span>
    </ScriptGoogleMapsOverlayView>
  </ScriptGoogleMaps>
</template>

Map Panning

When an overlay opens, the map automatically pans so the overlay is fully visible, matching the native InfoWindow behavior. The default padding is 40px from the map edge.

To customize the padding or disable panning:

<!-- Custom padding -->
<ScriptGoogleMapsOverlayView :pan-on-open="60">
  ...
</ScriptGoogleMapsOverlayView>

<!-- Disable panning -->
<ScriptGoogleMapsOverlayView :pan-on-open="false">
  ...
</ScriptGoogleMapsOverlayView>

Cluster Awareness

When used inside a ScriptGoogleMapsMarkerClusterer, overlay views automatically hide when their parent marker joins a cluster on zoom out. This prevents orphaned overlays from floating over cluster icons.

When its marker is clustered, the overlay updates v-model:open to false. The user will need to reopen the overlay (e.g. click the marker again) after zooming back in.

To disable this behavior:

<ScriptGoogleMapsMarkerClusterer>
  <ScriptGoogleMapsMarker :position="markerPosition">
    <ScriptGoogleMapsOverlayView :hide-when-clustered="false">
      ...
    </ScriptGoogleMapsOverlayView>
  </ScriptGoogleMapsMarker>
</ScriptGoogleMapsMarkerClusterer>

Transitions

The overlay sets a data-state attribute ("open" or "closed") on its content element, enabling CSS transitions and animations for enter/leave effects. When closing, the component waits for any running transitionend or animationend event before hiding the element.

Access the dataState via a template ref (it is exposed via defineExpose, not as a slot prop).

CSS Animation

The data-state attribute is automatically set on the overlay content element, so you can target it directly in CSS without needing to bind it manually.

<ScriptGoogleMapsOverlayView v-model:open="open">
  <div class="popup">
    Animated popup
  </div>
</ScriptGoogleMapsOverlayView>

<style>
.popup[data-state="open"] {
  animation: fadeIn 200ms ease-out;
}
.popup[data-state="closed"] {
  animation: fadeOut 150ms ease-in;
}
@keyframes fadeIn {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes fadeOut {
  from { opacity: 1; transform: scale(1); }
  to { opacity: 0; transform: scale(0.95); }
}
</style>

CSS Transition

<script setup lang="ts">
const overlayRef = ref()
</script>

<template>
  <ScriptGoogleMapsOverlayView ref="overlayRef" v-model:open="open">
    <div
      class="popup"
      :style="{
        opacity: overlayRef?.dataState === 'open' ? 1 : 0,
        transition: 'opacity 200ms ease',
      }"
    >
      Fade transition
    </div>
  </ScriptGoogleMapsOverlayView>
</template>
The blockMapInteraction prop (default true) calls google.maps.OverlayView.preventMapHitsAndGesturesFrom() to stop clicks, taps, and drags from propagating through the overlay to the map. Set it to false for non-interactive overlays like labels.