Google Maps Platform
"Google Maps Platform: Maps JavaScript API, Places, Geocoding, Directions, Street View, React (@vis.gl/react-google-maps)"
Google Maps Platform provides the most widely recognized mapping experience, backed by comprehensive global data coverage and tight integration with the broader Google Cloud ecosystem. The Maps JavaScript API renders raster and vector maps with a familiar UX that users already understand. The platform's strength lies in its data APIs -- Places, Geocoding, Directions, Distance Matrix, and Street View -- which expose Google's continuously updated POI and routing graph. In React apps, use `@vis.gl/react-google-maps` (the official Google-sponsored library) rather than legacy community wrappers. Keep API key usage disciplined: restrict keys by HTTP referrer and API scope, and proxy sensitive calls through your backend to avoid client-side key exposure.
## Key Points
- **Use Map IDs**: Cloud-based map styling requires a Map ID. Create one in the Google Cloud Console and pass it as the `mapId` prop. This also unlocks Advanced Markers.
- **Restrict API keys**: In the Cloud Console, restrict keys by HTTP referrer for client-side keys and by IP for server-side keys. Enable only the specific APIs each key needs.
- **Load libraries on demand**: Use `useMapsLibrary("places")` to lazy-load the Places library only when needed, keeping the initial bundle small.
- **Proxy server-side calls**: Never call Geocoding, Directions, or Distance Matrix APIs from the browser with a server key. Route those calls through your API to protect the key and add caching.
- **Cache geocoding results**: Google's terms allow caching geocoding results for up to 30 days. Store frequently looked-up addresses in your database to reduce API costs.
- **Use session tokens for Autocomplete**: Pass a `sessionToken` to Autocomplete requests to bundle the autocomplete + place details into a single billing session.
- **Prefer AdvancedMarker over Marker**: The legacy `Marker` class is deprecated. `AdvancedMarker` supports custom HTML content and better accessibility.
- **Loading the entire Maps JavaScript API synchronously in the HTML head**: This blocks page rendering. Use the `APIProvider` component or load the API with `async defer`.
- **Creating new service instances on every render**: `DirectionsService`, `PlacesService`, and `Geocoder` should be instantiated once and reused. Store them in refs or outside the component.
- **Ignoring API billing tiers**: Places Autocomplete + Place Details charges per session. Street View charges per panorama load. Monitor usage in the Cloud Console billing dashboard.
- **Using client-side geocoding for batch jobs**: The JavaScript Geocoding service has aggressive rate limits. Use the server-side Geocoding API with exponential backoff for batch operations.
- **Forgetting to clean up listeners and renderers**: Google Maps event listeners and overlay objects (DirectionsRenderer, HeatmapLayer) must be removed in useEffect cleanup to prevent memory leaks.skilldb get maps-geolocation-services-skills/Google Maps PlatformFull skill: 268 linesGoogle Maps Platform
Core Philosophy
Google Maps Platform provides the most widely recognized mapping experience, backed by comprehensive global data coverage and tight integration with the broader Google Cloud ecosystem. The Maps JavaScript API renders raster and vector maps with a familiar UX that users already understand. The platform's strength lies in its data APIs -- Places, Geocoding, Directions, Distance Matrix, and Street View -- which expose Google's continuously updated POI and routing graph. In React apps, use @vis.gl/react-google-maps (the official Google-sponsored library) rather than legacy community wrappers. Keep API key usage disciplined: restrict keys by HTTP referrer and API scope, and proxy sensitive calls through your backend to avoid client-side key exposure.
Setup
Install dependencies and load the API:
// npm install @vis.gl/react-google-maps
import { APIProvider, Map, Marker, InfoWindow } from "@vis.gl/react-google-maps";
const GOOGLE_MAPS_API_KEY = process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY!;
function App() {
return (
<APIProvider apiKey={GOOGLE_MAPS_API_KEY}>
<Map
defaultCenter={{ lat: 40.7128, lng: -74.006 }}
defaultZoom={12}
style={{ width: "100%", height: "100vh" }}
mapId="your-map-id" // required for advanced markers and cloud styling
/>
</APIProvider>
);
}
For server-side API calls (Geocoding, Directions), use the REST endpoints directly:
const GOOGLE_SERVER_KEY = process.env.GOOGLE_MAPS_SERVER_KEY!;
async function geocode(address: string): Promise<{ lat: number; lng: number } | null> {
const url = new URL("https://maps.googleapis.com/maps/api/geocode/json");
url.searchParams.set("address", address);
url.searchParams.set("key", GOOGLE_SERVER_KEY);
const res = await fetch(url);
const data = await res.json();
const location = data.results?.[0]?.geometry?.location;
return location ?? null;
}
Key Techniques
Advanced Markers
import { AdvancedMarker, Pin } from "@vis.gl/react-google-maps";
interface Store {
id: string;
name: string;
position: google.maps.LatLngLiteral;
category: "restaurant" | "retail" | "office";
}
function StoreMarkers({ stores }: { stores: Store[] }) {
const colorMap = { restaurant: "#e53e3e", retail: "#3182ce", office: "#38a169" };
return (
<>
{stores.map((store) => (
<AdvancedMarker key={store.id} position={store.position} title={store.name}>
<Pin
background={colorMap[store.category]}
borderColor="#1a202c"
glyphColor="#fff"
/>
</AdvancedMarker>
))}
</>
);
}
Places Autocomplete
import { useMapsLibrary } from "@vis.gl/react-google-maps";
import { useEffect, useRef, useState } from "react";
function PlaceAutocomplete({ onSelect }: { onSelect: (place: google.maps.places.PlaceResult) => void }) {
const inputRef = useRef<HTMLInputElement>(null);
const places = useMapsLibrary("places");
useEffect(() => {
if (!places || !inputRef.current) return;
const autocomplete = new places.Autocomplete(inputRef.current, {
fields: ["geometry", "name", "formatted_address"],
types: ["geocode", "establishment"],
});
autocomplete.addListener("place_changed", () => {
const place = autocomplete.getPlace();
if (place.geometry) onSelect(place);
});
}, [places, onSelect]);
return <input ref={inputRef} type="text" placeholder="Search for a place..." />;
}
Directions Service
import { useMapsLibrary, useMap } from "@vis.gl/react-google-maps";
import { useEffect, useState } from "react";
interface RouteInfo {
distance: string;
duration: string;
steps: google.maps.DirectionsStep[];
}
function useDirections(
origin: google.maps.LatLngLiteral | null,
destination: google.maps.LatLngLiteral | null
): RouteInfo | null {
const map = useMap();
const routes = useMapsLibrary("routes");
const [routeInfo, setRouteInfo] = useState<RouteInfo | null>(null);
useEffect(() => {
if (!routes || !map || !origin || !destination) return;
const service = new routes.DirectionsService();
const renderer = new routes.DirectionsRenderer({ map });
service.route(
{
origin,
destination,
travelMode: google.maps.TravelMode.DRIVING,
},
(result, status) => {
if (status === "OK" && result) {
renderer.setDirections(result);
const leg = result.routes[0].legs[0];
setRouteInfo({
distance: leg.distance?.text ?? "",
duration: leg.duration?.text ?? "",
steps: leg.steps,
});
}
}
);
return () => renderer.setMap(null);
}, [routes, map, origin, destination]);
return routeInfo;
}
Street View
import { useRef, useEffect } from "react";
import { useMapsLibrary } from "@vis.gl/react-google-maps";
function StreetViewPane({ position }: { position: google.maps.LatLngLiteral }) {
const containerRef = useRef<HTMLDivElement>(null);
const streetView = useMapsLibrary("streetView");
useEffect(() => {
if (!streetView || !containerRef.current) return;
new google.maps.StreetViewPanorama(containerRef.current, {
position,
pov: { heading: 165, pitch: 0 },
zoom: 1,
});
}, [streetView, position]);
return <div ref={containerRef} style={{ width: "100%", height: 400 }} />;
}
Drawing on the Map
function useDrawingManager(
onPolygonComplete: (polygon: google.maps.Polygon) => void
) {
const map = useMap();
const drawing = useMapsLibrary("drawing");
useEffect(() => {
if (!drawing || !map) return;
const manager = new drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [google.maps.drawing.OverlayType.POLYGON],
},
polygonOptions: {
fillColor: "#3182ce",
fillOpacity: 0.3,
strokeWeight: 2,
editable: true,
},
});
manager.setMap(map);
google.maps.event.addListener(manager, "polygoncomplete", onPolygonComplete);
return () => {
manager.setMap(null);
google.maps.event.clearInstanceListeners(manager);
};
}, [drawing, map, onPolygonComplete]);
}
Heatmap Layer
function useHeatmap(points: google.maps.LatLngLiteral[]) {
const map = useMap();
const visualization = useMapsLibrary("visualization");
useEffect(() => {
if (!visualization || !map || points.length === 0) return;
const heatmap = new google.maps.visualization.HeatmapLayer({
data: points.map((p) => new google.maps.LatLng(p.lat, p.lng)),
radius: 30,
opacity: 0.7,
});
heatmap.setMap(map);
return () => heatmap.setMap(null);
}, [visualization, map, points]);
}
Best Practices
- Use Map IDs: Cloud-based map styling requires a Map ID. Create one in the Google Cloud Console and pass it as the
mapIdprop. This also unlocks Advanced Markers. - Restrict API keys: In the Cloud Console, restrict keys by HTTP referrer for client-side keys and by IP for server-side keys. Enable only the specific APIs each key needs.
- Load libraries on demand: Use
useMapsLibrary("places")to lazy-load the Places library only when needed, keeping the initial bundle small. - Proxy server-side calls: Never call Geocoding, Directions, or Distance Matrix APIs from the browser with a server key. Route those calls through your API to protect the key and add caching.
- Cache geocoding results: Google's terms allow caching geocoding results for up to 30 days. Store frequently looked-up addresses in your database to reduce API costs.
- Use session tokens for Autocomplete: Pass a
sessionTokento Autocomplete requests to bundle the autocomplete + place details into a single billing session. - Prefer AdvancedMarker over Marker: The legacy
Markerclass is deprecated.AdvancedMarkersupports custom HTML content and better accessibility.
Anti-Patterns
- Loading the entire Maps JavaScript API synchronously in the HTML head: This blocks page rendering. Use the
APIProvidercomponent or load the API withasync defer. - Creating new service instances on every render:
DirectionsService,PlacesService, andGeocodershould be instantiated once and reused. Store them in refs or outside the component. - Ignoring API billing tiers: Places Autocomplete + Place Details charges per session. Street View charges per panorama load. Monitor usage in the Cloud Console billing dashboard.
- Using client-side geocoding for batch jobs: The JavaScript Geocoding service has aggressive rate limits. Use the server-side Geocoding API with exponential backoff for batch operations.
- Forgetting to clean up listeners and renderers: Google Maps event listeners and overlay objects (DirectionsRenderer, HeatmapLayer) must be removed in useEffect cleanup to prevent memory leaks.
- Hardcoding coordinates instead of using the Geocoding API: Coordinates drift as addresses change. Geocode at display time or re-geocode periodically for mission-critical location data.
Install this skill directly: skilldb add maps-geolocation-services-skills
Related Skills
HERE Maps
HERE Maps Platform: Maps API for JavaScript, geocoding, routing, isoline routing, fleet telematics, and platform data services
Leaflet
"Leaflet: open-source maps, markers, popups, layers, GeoJSON, plugins, React (react-leaflet), tile providers"
Mapbox
"Mapbox: interactive maps, GL JS, geocoding, directions, markers, layers, 3D terrain, React (react-map-gl)"
OpenLayers
OpenLayers: high-performance open-source map library with vector tiles, projections, OGC services (WMS/WFS), drawing interactions, and GeoJSON/KML support
OpenStreetMap & Nominatim
OpenStreetMap tile usage and Nominatim geocoding API: forward/reverse geocoding, tile servers, Overpass API queries, and attribution requirements
Radar
"Radar: geofencing, geocoding, trip tracking, address validation, maps, fraud detection, JavaScript SDK"