Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
54daa72
feat(googleMaps): add composable SFCs
DamianGlowala Oct 16, 2024
c4f49da
feat: make SFC's options reactive
DamianGlowala Oct 17, 2024
5d990f8
fix: address reactivity issues by using `shallowRef`
DamianGlowala Oct 18, 2024
f811c93
feat: add SFCs playground
DamianGlowala Oct 18, 2024
23dfd26
fix: allow mounting info window to map when not within marker
DamianGlowala Oct 18, 2024
9c8d2d0
refactor: simplify clearing instance listeners
DamianGlowala Oct 18, 2024
dee6917
refactor: modify the way event listeners are registered
DamianGlowala Oct 18, 2024
81ada17
feat: add advanced marker element
DamianGlowala Oct 18, 2024
22bfd24
fix: add options reactivity to advanced marker element
DamianGlowala Oct 18, 2024
1ae0c9b
feat: add pin element component
DamianGlowala Oct 20, 2024
fae7d86
refactor: modify event listeners registration for advanced marker ele…
DamianGlowala Oct 22, 2024
f7bc989
refactor: use `mapsApi` for instantiation and clearing event listeners
DamianGlowala Oct 25, 2024
397af2b
chore: sync dependencies
DamianGlowala Oct 25, 2024
682d6d9
Merge branch 'main' into feat/google-maps-sfcs
DamianGlowala Oct 25, 2024
8636a3d
fix: sync lockfile
DamianGlowala Oct 25, 2024
099a6d6
Merge branch 'main' of github.com:nuxt/scripts into feat/google-maps-…
harlan-zw Nov 12, 2024
0cf13ff
chore: sync lock
harlan-zw Nov 12, 2024
934dc66
fix: move to dedicated GoogleMaps folder
harlan-zw Nov 12, 2024
69df312
perf: avoid redundant rerenders of clusterer when removing large amou…
DamianGlowala Jan 15, 2025
babf38e
Merge branch 'main' of github.com:nuxt/scripts into feat/google-maps-…
harlan-zw Sep 19, 2025
e2a39b8
Merge branch 'feat/google-maps-sfcs' of github.com:nuxt/scripts into …
harlan-zw Sep 22, 2025
7a01780
chore: tidy up PR
harlan-zw Sep 22, 2025
b4f5a6c
chore: tidy up PR
harlan-zw Sep 22, 2025
2a3cdd2
Merge branch 'main' of github.com:nuxt/scripts into feat/google-maps-…
harlan-zw Sep 22, 2025
6d2b6cc
chore: sync lock
harlan-zw Sep 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 272 additions & 0 deletions docs/content/scripts/content/google-maps.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,278 @@ By providing your own placeholder slot you will disable the default placeholder
</template>
```

## Google Maps SFC Components

Nuxt Scripts provides individual Single File Components (SFCs) for different Google Maps elements. These components allow you to declaratively compose complex maps using Vue's template syntax.

### Installation

To use marker clustering functionality, you'll need to install the required peer dependency:

```bash
npm install @googlemaps/markerclusterer
# or
yarn add @googlemaps/markerclusterer
# or
pnpm add @googlemaps/markerclusterer
```

### Available Components

All Google Maps SFC components must be used within a `<ScriptGoogleMaps>` component:

- `<ScriptGoogleMapsMarker>` - Classic markers with icon support
- `<ScriptGoogleMapsAdvancedMarkerElement>` - Modern advanced markers with HTML content
- `<ScriptGoogleMapsPinElement>` - Customizable pin markers (use within AdvancedMarkerElement)
- `<ScriptGoogleMapsInfoWindow>` - Information windows that appear on click
- `<ScriptGoogleMapsMarkerClusterer>` - Groups nearby markers into clusters
- `<ScriptGoogleMapsCircle>` - Circular overlays
- `<ScriptGoogleMapsPolygon>` - Polygon shapes
- `<ScriptGoogleMapsPolyline>` - Line paths
- `<ScriptGoogleMapsRectangle>` - Rectangular overlays
- `<ScriptGoogleMapsHeatmapLayer>` - Heatmap visualization

### Basic Usage

```vue
<template>
<ScriptGoogleMaps
:center="{ lat: -34.397, lng: 150.644 }"
:zoom="8"
api-key="your-api-key"
>
<!-- Add markers -->
<ScriptGoogleMapsMarker
:options="{ position: { lat: -34.397, lng: 150.644 } }"
>
<!-- Info window appears on marker click -->
<ScriptGoogleMapsInfoWindow>
<div>
<h3>Sydney, Australia</h3>
<p>A great city!</p>
</div>
</ScriptGoogleMapsInfoWindow>
</ScriptGoogleMapsMarker>

<!-- Advanced marker with custom pin -->
<ScriptGoogleMapsAdvancedMarkerElement
:options="{ position: { lat: -34.407, lng: 150.654 } }"
>
<ScriptGoogleMapsPinElement
:options="{ scale: 1.5, background: '#FF0000' }"
/>
</ScriptGoogleMapsAdvancedMarkerElement>

<!-- Circle overlay -->
<ScriptGoogleMapsCircle
:options="{
center: { lat: -34.397, lng: 150.644 },
radius: 1000,
fillColor: '#FF0000',
fillOpacity: 0.35
}"
/>
</ScriptGoogleMaps>
</template>
```

### Component Composition Patterns

**Marker Clustering**

```vue
<template>
<ScriptGoogleMaps api-key="your-api-key">
<ScriptGoogleMapsMarkerClusterer>
<ScriptGoogleMapsMarker
v-for="location in locations"
:key="location.id"
:options="{ position: location.position }"
>
<ScriptGoogleMapsInfoWindow>
<div>{{ location.name }}</div>
</ScriptGoogleMapsInfoWindow>
</ScriptGoogleMapsMarker>
</ScriptGoogleMapsMarkerClusterer>
</ScriptGoogleMaps>
</template>
```

**Heatmap with Data Points**

```vue
<script setup>
const heatmapData = ref([])

onMounted(() => {
// Populate heatmap data with google.maps.LatLng objects
})
</script>

<template>
<ScriptGoogleMaps api-key="your-api-key">
<ScriptGoogleMapsHeatmapLayer
:options="{ data: heatmapData }"
/>
</ScriptGoogleMaps>
</template>
```

**See the [SFC Playground Example](https://nuxt-scripts-playground.stackblitz.io/third-parties/google-maps/sfcs) for a comprehensive demonstration.**

### Component Details

#### ScriptGoogleMapsMarker

Classic Google Maps marker with icon support.

**Props:**
- `options` - `google.maps.MarkerOptions` (excluding `map`)

**Events:**
- Standard marker events: `click`, `mousedown`, `mouseover`, etc.

#### ScriptGoogleMapsAdvancedMarkerElement

Modern advanced markers that support HTML content and better customization.

**Props:**
- `options` - `google.maps.marker.AdvancedMarkerElementOptions` (excluding `map`)

**Events:**
- Standard marker events: `click`, `drag`, `position_changed`, etc.

#### ScriptGoogleMapsInfoWindow

Information windows that display content when triggered.

**Props:**
- `options` - `google.maps.InfoWindowOptions`

**Behavior:**
- Automatically opens on parent marker click
- Can be used standalone with explicit position
- Supports custom HTML content via default slot

#### ScriptGoogleMapsMarkerClusterer

Groups nearby markers into clusters for better performance and UX.

**Props:**
- `options` - `MarkerClustererOptions` (excluding `map`)

**Dependencies:**
- Requires `@googlemaps/markerclusterer` peer dependency

#### Other Components

- **ScriptGoogleMapsPinElement**: Use within AdvancedMarkerElement for customizable pins
- **ScriptGoogleMapsCircle**: Circular overlays with radius and styling
- **ScriptGoogleMapsPolygon/Polyline**: Shape and line overlays
- **ScriptGoogleMapsRectangle**: Rectangular overlays
- **ScriptGoogleMapsHeatmapLayer**: Data visualization with heatmaps

All components support:
- Reactive `options` prop that updates the underlying Google Maps object
- Automatic cleanup on component unmount
- TypeScript support with Google Maps types

### Best Practices

#### Performance Considerations

**Use MarkerClusterer for Many Markers**
```vue
<!-- βœ… Good: Use clusterer for >10 markers -->
<ScriptGoogleMapsMarkerClusterer>
<ScriptGoogleMapsMarker v-for="marker in manyMarkers" />
</ScriptGoogleMapsMarkerClusterer>

<!-- ❌ Avoid: Many individual markers -->
<ScriptGoogleMapsMarker v-for="marker in manyMarkers" />
```

**Prefer AdvancedMarkerElement for Modern Apps**
```vue
<!-- βœ… Recommended: Better performance and styling -->
<ScriptGoogleMapsAdvancedMarkerElement :options="options">
<ScriptGoogleMapsPinElement :options="{ background: '#FF0000' }" />
</ScriptGoogleMapsAdvancedMarkerElement>

<!-- ⚠️ Legacy: Use only when advanced markers aren't supported -->
<ScriptGoogleMapsMarker :options="options" />
```

#### Component Hierarchy

Components must follow this nesting structure:

```
ScriptGoogleMaps (root)
β”œβ”€β”€ ScriptGoogleMapsMarkerClusterer (optional)
β”‚ └── ScriptGoogleMapsMarker/AdvancedMarkerElement
β”‚ └── ScriptGoogleMapsInfoWindow (optional)
β”œβ”€β”€ ScriptGoogleMapsAdvancedMarkerElement
β”‚ β”œβ”€β”€ ScriptGoogleMapsPinElement (optional)
β”‚ └── ScriptGoogleMapsInfoWindow (optional)
└── Other overlays (Circle, Polygon, etc.)
```

#### Reactive Data Patterns

**Reactive Marker Updates**
```vue
<script setup>
const markers = ref([
{ id: 1, position: { lat: -34.397, lng: 150.644 }, title: 'Sydney' }
])

// Markers automatically update when data changes
function addMarker() {
markers.value.push({
id: Date.now(),
position: getRandomPosition(),
title: 'New Location'
})
}
</script>

<template>
<ScriptGoogleMaps>
<ScriptGoogleMapsMarker
v-for="marker in markers"
:key="marker.id"
:options="{ position: marker.position, title: marker.title }"
/>
</ScriptGoogleMaps>
</template>
```

#### Error Handling

Always provide error fallbacks and loading states:

```vue
<script setup>
const mapError = ref(false)
</script>

<template>
<ScriptGoogleMaps
@error="mapError = true"
api-key="your-api-key"
>
<template #error>
<div class="p-4 bg-red-100">
Failed to load Google Maps
</div>
</template>

<!-- Your components -->
</ScriptGoogleMaps>
</template>
```

## useScriptGoogleMaps

The `useScriptGoogleMaps` composable lets you have fine-grain control over the Google Maps SDK. It provides a way to load the Google Maps SDK and interact with it programmatically.
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export default createConfigForNuxt({
'vue/no-multiple-template-root': 'off',
// NOTE: Disable this style rules if stylistic is not enabled
'vue/max-attributes-per-line': 'off',
// Disabled for Google Maps SFC components that use conditional rendering without root elements
'vue/valid-template-root': 'off',
},
})
.append({
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@
]
},
"peerDependencies": {
"@stripe/stripe-js": "^7.0.0",
"@googlemaps/markerclusterer": "^2.6.2",
"@paypal/paypal-js": "^8.1.2",
"@stripe/stripe-js": "^7.0.0",
"@types/google.maps": "^3.58.1",
"@types/vimeo__player": "^2.18.3",
"@types/youtube": "^0.1.0",
Expand Down Expand Up @@ -124,6 +125,7 @@
"@paypal/paypal-js": "^8.4.2",
"@types/semver": "^7.7.1",
"@typescript-eslint/typescript-estree": "^8.44.0",
"@vue/test-utils": "^2.4.6",
"acorn-loose": "^8.5.2",
"bumpp": "^10.2.3",
"changelogen": "^0.6.2",
Expand Down
Loading
Loading