Skip to content

Commit 5a8e644

Browse files
authored
feat(google-maps): DX overhaul for v1 (#659)
1 parent 7da4f1a commit 5a8e644

35 files changed

+1895
-177
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
title: Google Maps
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
title: Guides
2+
icon: i-ph-book-duotone
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
title: Performance
3+
---
4+
5+
`ScriptGoogleMaps` is optimized by default: the JavaScript API only loads when the user interacts with the map. Before that, a lightweight static image placeholder is shown.
6+
7+
## Loading Strategies
8+
9+
### Default: Lazy (Recommended)
10+
11+
By default, the map loads on `mouseenter`, `mouseover`, or `mousedown`. Most page visitors never interact with the map, so you avoid the Maps JavaScript API charge for those sessions.
12+
13+
```vue
14+
<template>
15+
<!-- Loads JS API on first hover/click -->
16+
<ScriptGoogleMaps
17+
:center="{ lat: -33.8688, lng: 151.2093 }"
18+
:zoom="12"
19+
/>
20+
</template>
21+
```
22+
23+
### Immediate Loading
24+
25+
Forces the JavaScript API to load with the page. Use this only when the map is the primary content and you need it interactive from the start.
26+
27+
```vue
28+
<template>
29+
<ScriptGoogleMaps
30+
trigger="immediate"
31+
:center="{ lat: -33.8688, lng: 151.2093 }"
32+
:zoom="12"
33+
/>
34+
</template>
35+
```
36+
37+
::callout{color="amber"}
38+
Immediate loading charges the Maps JavaScript API ($7/1000) on every page view. Only use this when the map is essential to the page experience.
39+
::
40+
41+
### Custom Triggers
42+
43+
You can control exactly when the map loads using any [Element Event Trigger](/docs/guides/script-triggers#element-event-triggers).
44+
45+
```vue
46+
<template>
47+
<!-- Only load on explicit click -->
48+
<ScriptGoogleMaps trigger="mousedown" />
49+
50+
<!-- Load when element enters viewport -->
51+
<ScriptGoogleMaps trigger="visible" />
52+
</template>
53+
```
54+
55+
## Placeholder Optimization
56+
57+
### Above the Fold
58+
59+
When the map is visible without scrolling, mark it for priority loading. This sets the placeholder image to `loading="eager"` and adds a `preconnect` hint.
60+
61+
```vue
62+
<template>
63+
<ScriptGoogleMaps above-the-fold />
64+
</template>
65+
```
66+
67+
### Custom Placeholder
68+
69+
Replace the default Google Static Maps image with your own custom image to avoid Static Maps API charges. Useful when you have a screenshot or illustration of the map area.
70+
71+
```vue
72+
<template>
73+
<ScriptGoogleMaps>
74+
<template #placeholder>
75+
<img src="/map-preview.webp" alt="Map of Sydney" style="width: 100%; height: 100%; object-fit: cover;">
76+
</template>
77+
</ScriptGoogleMaps>
78+
</template>
79+
```
80+
81+
You can also access the generated static map URL if you want to use it with custom styling. Note that rendering the `placeholder` URL still makes a Static Maps API request and incurs charges:
82+
83+
```vue
84+
<template>
85+
<ScriptGoogleMaps>
86+
<template #placeholder="{ placeholder }">
87+
<img :src="placeholder" alt="Map" style="filter: grayscale(1);">
88+
</template>
89+
</ScriptGoogleMaps>
90+
</template>
91+
```
92+
93+
### Loading State
94+
95+
Show a custom indicator while the JavaScript API loads:
96+
97+
```vue
98+
<template>
99+
<ScriptGoogleMaps>
100+
<template #loading>
101+
<div style="display: flex; align-items: center; justify-content: center; height: 100%;">
102+
Loading map...
103+
</div>
104+
</template>
105+
</ScriptGoogleMaps>
106+
</template>
107+
```
108+
109+
## Marker Performance
110+
111+
When rendering many markers, use `ScriptGoogleMapsMarkerClusterer` to group nearby markers. This significantly reduces DOM elements and improves pan/zoom performance.
112+
113+
```vue
114+
<template>
115+
<ScriptGoogleMaps :center="center" :zoom="10">
116+
<ScriptGoogleMapsMarkerClusterer>
117+
<ScriptGoogleMapsAdvancedMarkerElement
118+
v-for="place in places"
119+
:key="place.id"
120+
:position="place.position"
121+
/>
122+
</ScriptGoogleMapsMarkerClusterer>
123+
</ScriptGoogleMaps>
124+
</template>
125+
```
126+
127+
See [Billing & Permissions](/scripts/google-maps/guides/billing) for a full cost breakdown and optimization strategies.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
---
2+
title: Programmatic API
3+
---
4+
5+
The `ScriptGoogleMaps` component exposes its internal APIs via template ref, giving you full control beyond what the declarative SFC components provide.
6+
7+
## Accessing the API
8+
9+
```vue
10+
<script setup lang="ts">
11+
const mapRef = ref()
12+
13+
function onReady({ googleMaps, map, createAdvancedMapMarker, resolveQueryToLatLng, importLibrary }) {
14+
// All APIs available here
15+
}
16+
</script>
17+
18+
<template>
19+
<ScriptGoogleMaps ref="mapRef" @ready="onReady" />
20+
</template>
21+
```
22+
23+
### Exposed Properties
24+
25+
| Property | Type | Description |
26+
|---|---|---|
27+
| `googleMaps` | `Ref<typeof google.maps>`{lang="html"} | The Google Maps API namespace |
28+
| `map` | `ShallowRef<google.maps.Map>`{lang="html"} | The map instance |
29+
| `createAdvancedMapMarker` | `(options?) => Promise<AdvancedMarkerElement>`{lang="html"} | Create a marker programmatically |
30+
| `resolveQueryToLatLng` | `(query: string) => Promise<LatLng>`{lang="html"} | Geocode a location string |
31+
| `importLibrary` | `(key: string) => Promise<any>`{lang="html"} | Load an additional Google Maps library |
32+
33+
## Creating Markers Programmatically
34+
35+
For cases where declarative `v-for` markers aren't flexible enough (dynamic data, imperative creation logic), use `createAdvancedMapMarker`:
36+
37+
```vue
38+
<script setup lang="ts">
39+
const mapRef = ref()
40+
41+
async function addMarkerAtCenter() {
42+
const map = mapRef.value.map.value
43+
const center = map.getCenter()
44+
const marker = await mapRef.value.createAdvancedMapMarker({
45+
position: { lat: center.lat(), lng: center.lng() },
46+
title: 'New Marker',
47+
})
48+
}
49+
</script>
50+
51+
<template>
52+
<ScriptGoogleMaps ref="mapRef" :center="{ lat: -33.8688, lng: 151.2093 }" :zoom="12" />
53+
<button @click="addMarkerAtCenter">
54+
Add Marker at Center
55+
</button>
56+
</template>
57+
```
58+
59+
::callout
60+
For most use cases, prefer the declarative `ScriptGoogleMapsAdvancedMarkerElement` component with `v-for`. Use the programmatic API when you need fine-grained control over marker lifecycle or are integrating with external data sources.
61+
::
62+
63+
## Geocoding Queries
64+
65+
Convert location strings to coordinates using `resolveQueryToLatLng`. When you enable the registry proxy, this resolves server-side (cheaper, API key hidden). Otherwise it falls back to the client-side Places API.
66+
67+
```vue
68+
<script setup lang="ts">
69+
const mapRef = ref()
70+
71+
async function goToLocation(query: string) {
72+
const latLng = await mapRef.value.resolveQueryToLatLng(query)
73+
mapRef.value.map.value.setCenter(latLng)
74+
mapRef.value.map.value.setZoom(15)
75+
}
76+
</script>
77+
78+
<template>
79+
<ScriptGoogleMaps ref="mapRef" :center="{ lat: 0, lng: 0 }" :zoom="2" />
80+
<button @click="goToLocation('Eiffel Tower, Paris')">
81+
Go to Paris
82+
</button>
83+
</template>
84+
```
85+
86+
## Importing Libraries
87+
88+
Google Maps splits functionality into libraries that load on demand. Use `importLibrary` to access geometry, drawing, places, and visualization APIs:
89+
90+
```vue
91+
<script setup lang="ts">
92+
const mapRef = ref()
93+
94+
async function measureDistance(a: google.maps.LatLng, b: google.maps.LatLng) {
95+
const geometry = await mapRef.value.importLibrary('geometry')
96+
return geometry.spherical.computeDistanceBetween(a, b)
97+
}
98+
</script>
99+
```
100+
101+
Available libraries: `marker`, `places`, `geometry`, `drawing`, `visualization`
102+
103+
## Subscribing to Map Events
104+
105+
Use the `@ready` event to access the map instance and subscribe to native Google Maps events:
106+
107+
```vue
108+
<script setup lang="ts">
109+
function onReady({ map }) {
110+
watch(map, (m) => {
111+
if (!m)
112+
return
113+
m.addListener('zoom_changed', () => {
114+
console.log('Zoom:', m.getZoom())
115+
})
116+
m.addListener('center_changed', () => {
117+
console.log('Center:', m.getCenter()?.toJSON())
118+
})
119+
m.addListener('idle', () => {
120+
// Map finished moving, good time to fetch data for visible bounds
121+
const bounds = m.getBounds()
122+
})
123+
}, { immediate: true })
124+
}
125+
</script>
126+
127+
<template>
128+
<ScriptGoogleMaps @ready="onReady" />
129+
</template>
130+
```
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
title: Map Styling
3+
---
4+
5+
Google Maps supports two styling approaches: legacy JSON styles and cloud-based map IDs. Both work with Nuxt Scripts, including the static map placeholder.
6+
7+
## JSON Styles
8+
9+
Use the `mapOptions.styles` prop with a JSON style array. You can find pre-made styles on [Snazzy Maps](https://snazzymaps.com/).
10+
11+
Styles automatically apply to both the static map placeholder and the interactive map.
12+
13+
```vue
14+
<script setup lang="ts">
15+
const mapOptions = {
16+
styles: [
17+
{ elementType: 'labels', stylers: [{ visibility: 'off' }, { color: '#f49f53' }] },
18+
{ featureType: 'landscape', stylers: [{ color: '#f9ddc5' }, { lightness: -7 }] },
19+
{ featureType: 'road', stylers: [{ color: '#813033' }, { lightness: 43 }] },
20+
{ featureType: 'water', stylers: [{ color: '#1994bf' }, { saturation: -69 }, { gamma: 0.99 }, { lightness: 43 }] },
21+
{ featureType: 'poi.park', stylers: [{ color: '#645c20' }, { lightness: 39 }] },
22+
],
23+
}
24+
</script>
25+
26+
<template>
27+
<ScriptGoogleMaps :map-options="mapOptions" />
28+
</template>
29+
```
30+
31+
## Cloud-Based Map IDs
32+
33+
Google's [Map Styling](https://developers.google.com/maps/documentation/cloud-customization) lets you create and manage styles in the Google Cloud Console, then apply them with a map ID.
34+
35+
```vue
36+
<template>
37+
<ScriptGoogleMaps
38+
:map-options="{ mapId: 'YOUR_MAP_ID' }"
39+
:center="{ lat: -33.8688, lng: 151.2093 }"
40+
:zoom="12"
41+
/>
42+
</template>
43+
```
44+
45+
::callout{color="amber"}
46+
JSON `styles` and `mapId` are mutually exclusive. When you provide both, the component ignores `mapId` and applies `styles`. Note that `AdvancedMarkerElement` requires a map ID to function; legacy `Marker` works without one.
47+
::
48+
49+
## Dark Mode / Color Mode
50+
51+
Switch map styles automatically based on the user's color mode preference. Provide a `mapIds` object with light and dark map IDs:
52+
53+
```vue
54+
<template>
55+
<ScriptGoogleMaps
56+
:map-ids="{ light: 'LIGHT_MAP_ID', dark: 'DARK_MAP_ID' }"
57+
:center="{ lat: -33.8688, lng: 151.2093 }"
58+
:zoom="12"
59+
/>
60+
</template>
61+
```
62+
63+
This auto-detects `@nuxtjs/color-mode` if installed. You can also control it manually with the `colorMode` prop:
64+
65+
```vue
66+
<script setup lang="ts">
67+
const isDark = ref(false)
68+
</script>
69+
70+
<template>
71+
<ScriptGoogleMaps
72+
:map-ids="{ light: 'LIGHT_MAP_ID', dark: 'DARK_MAP_ID' }"
73+
:color-mode="isDark ? 'dark' : 'light'"
74+
/>
75+
<button @click="isDark = !isDark">
76+
Toggle Dark Mode
77+
</button>
78+
</template>
79+
```
80+
81+
## Combining Styles with Markers
82+
83+
Custom-styled maps pair well with custom marker content for a cohesive look:
84+
85+
```vue
86+
<template>
87+
<ScriptGoogleMaps
88+
:map-options="{ mapId: 'DARK_THEME_ID' }"
89+
:center="{ lat: -33.8688, lng: 151.2093 }"
90+
:zoom="13"
91+
>
92+
<ScriptGoogleMapsAdvancedMarkerElement
93+
v-for="place in places"
94+
:key="place.id"
95+
:position="place.position"
96+
>
97+
<template #content>
98+
<div style="background: rgba(255,255,255,0.9); padding: 4px 8px; border-radius: 4px; font-size: 12px;">
99+
{{ place.label }}
100+
</div>
101+
</template>
102+
</ScriptGoogleMapsAdvancedMarkerElement>
103+
</ScriptGoogleMaps>
104+
</template>
105+
```

0 commit comments

Comments
 (0)