Modern implementation guide using @vis.gl/react-google-maps and Next.js App Router
We use the official Google-maintained React wrapper for the best performance and developer experience.
npm install @vis.gl/react-google-mapsCreate or edit your .env.local file in the root of your project. Use the
NEXT_PUBLIC_ prefix so the key is available on the client side.
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_actual_api_key_hereIn Next.js App Router, components using interactive maps must be Client Components.
'use client';
import { APIProvider, Map, Marker } from '@vis.gl/react-google-maps';
export default function GoogleMap() {
const API_KEY = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY;
return (
<APIProvider apiKey={API_KEY}>
<div style={{ height: '400px', width: '100%' }}>
<Map
defaultCenter={{ lat: -34.397, lng: 150.644 }}
defaultZoom={12}
gestureHandling={'greedy'}
disableDefaultUI={false}
>
<Marker position={{ lat: -34.397, lng: 150.644 }} />
</Map>
</div>
</APIProvider>
);
}layout.tsx (or a dedicated provider component) with <APIProvider> to
load the Google Maps script only once for the entire app.
When using @vis.gl/react-google-maps, you should prioritize Declarative
Components. Avoid using new google.maps.* inside useEffect unless
absolutely necessary.
Standard features like Markers, InfoWindows, Polygons, and Circles should be used as React components. This allows React to manage their lifecycle and props automatically.
<Map>
<Marker position={pos} />
<Polygon paths={paths} fillColor="#ff0000" />
<Circle center={center} radius={1000} />
</Map>Only use the useMap() hook when you need to call methods directly on the map
instance (e.g., map.panTo(), map.fitBounds()) or when a specific
feature doesn't have a React component yet.
const map = useMap();
useEffect(() => {
if (map) {
map.setHeading(45); // Direct API call
}
}, [map]);| Variable Name | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY |
Yes | The browser-side API key for rendering the map. |
GOOGLE_MAPS_SERVER_KEY |
Optional | A restricted key for server-side API calls (like Geocoding from a Route Handler). |
localhost:3000/* and
yourdomain.com/*).
Create and render a map on your page with custom center and zoom level
Initialize a map by providing a DOM element and configuration options.
<Map
defaultCenter={{ lat: -34.397, lng: 150.644 }}
defaultZoom={8}
/>Add clickable markers at specific coordinates with titles and info windows
Place markers on the map to identify specific locations.
<Marker
position={{ lat: -34.397, lng: 150.644 }}
title="Hello World!"
/>Display popup information when markers are clicked
Show descriptive text or HTML in a popup over the map.
<InfoWindow position={{ lat: -34.397, lng: 150.644 }}>
<h3>Location Info</h3>
<p>Details here...</p>
</InfoWindow>Draw connected lines between multiple points (routes, paths)
Draw linear paths between a series of coordinates.
// Recommended: Use the Polyline component
<Map>
<Polyline
path={[{lat: 37, lng: -122}, {lat: 38, lng: -123}]}
strokeColor="#FF0000"
strokeWeight={2}
/>
</Map>Create closed shapes with fill and stroke styles
Define an enclosed area on the map with custom styling.
// Recommended: Use the Polygon component
<Map>
<Polygon
paths={[{lat: 37, lng: -122}, {lat: 38, lng: -122}, {lat: 38, lng: -121}]}
fillColor="#00FF00"
strokeColor="#00FF00"
/>
</Map>Draw circular areas with customizable radius and styling
Create a circle around a center point with a specific radius in meters.
// Recommended: Use the Circle component
<Map>
<Circle
center={{ lat: -34.397, lng: 150.644 }}
radius={1000}
fillColor="#FF0000"
/>
</Map>Create rectangular boundaries on the map
Define a rectangle using north, south, east, and west boundaries.
// Recommended: Use the Rectangle component
<Map>
<Rectangle
bounds={{ north: 38, south: 37, east: -122, west: -123 }}
fillColor="#0000FF"
/>
</Map>Switch between roadmap, satellite, terrain, and hybrid views
Change the visual style of the map tiles.
<Map defaultMapTypeId="satellite" />Enable/disable zoom buttons and set min/max zoom levels
Configure how users can zoom in and out.
<Map
zoomControl={true}
minZoom={5}
maxZoom={15}
/>Allow users to move around the map with directional controls
Enable the UI control for panning the map.
<Map panControl={true} />Embed 360Β° street-level imagery from Google Street View
Initialize a Street View panorama in a div.
// Pattern: Create a component or use useEffect
const map = useMap();
useEffect(() => {
if (!map) return;
const panorama = new google.maps.StreetViewPanorama(
document.getElementById('pano'),
{ position: {lat: 42, lng: -71} }
);
map.setStreetView(panorama);
}, [map]);Apply custom styling to map appearance (colors, labels, features)
Use a JSON array to customize the visual appearance of map elements.
<Map
styles={[{ elementType: 'geometry', stylers: [{ color: '#ebe3cd' }] }]}
/>Get user's current location using browser geolocation API
Retrieve the device's current GPS coordinates.
// Use inside a useEffect or event handler
useEffect(() => {
navigator.geolocation.getCurrentPosition(pos => {
setCenter({ lat: pos.coords.latitude, lng: pos.coords.longitude });
});
}, []);Automatically fit map to show all markers or polygons
Extend a LatLngBounds object and fit the map viewport to it.
const map = useMap();
useEffect(() => {
if (!map) return;
const bounds = new google.maps.LatLngBounds();
points.forEach(p => bounds.extend(p));
map.fitBounds(bounds);
}, [map, points]);Group nearby markers for better visualization of large datasets
Use the MarkerClusterer library to manage large numbers of markers.
new MarkerClusterer(map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
});Use custom images or symbols instead of default markers
Define an icon property in the Marker constructor.
<Marker
position={{ lat: -34, lng: 150 }}
icon="https://example.com/marker.png"
/>Add drop, bounce, and fade animations to markers
Set the animation property to BOUNCE or DROP.
<Marker animation={google.maps.Animation.BOUNCE} />
Handle click, drag, zoom, and other map events
Register callback functions for specific UI interactions.
<Map onClick={(e) => console.log(e.detail.latLng)} />
Visualize intensity of point data across geographic areas
Use HeatmapLayer from the visualization library.
const heatmap = new google.maps.visualization.HeatmapLayer({
data: [new google.maps.LatLng(37.7, -122.4), ...],
map: map
});Import and display geographic data from KML and GeoJSON files
Load external spatial data files directly onto the map.
const map = useMap();
useEffect(() => {
if (map) map.data.loadGeoJson(url);
}, [map]);Convert addresses to coordinates and vice versa
Use the Geocoder service to find coordinates for an address.
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: 'New York' }, (results, status) => {
if (status === 'OK') map.setCenter(results[0].geometry.location);
});Calculate distances between multiple origin/destination pairs
Compute distance and duration between sets of points.
const service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
origins: ['Seattle'], destinations: ['Portland'],
travelMode: 'DRIVING'
}, callback);Get turn-by-turn directions and route alternatives
Generate routes and display them using DirectionsRenderer.
const directionsService = new google.maps.DirectionsService();
directionsService.route({
origin: 'Chicago', destination: 'St Louis',
travelMode: 'DRIVING'
}, (response, status) => { ... });Create maps that adapt to different screen sizes
Trigger a resize event when the container size changes.
window.addEventListener('resize', () => {
google.maps.event.trigger(map, 'resize');
});Toggle visibility of map layers dynamically
Show or hide specific layers by setting the map property.
layer.setMap(isVisible ? map : null);Display real-time traffic information on the map
Overlay current traffic flow data.
const trafficLayer = new google.maps.TrafficLayer();
trafficLayer.setMap(map);Show public transportation routes (buses, trains, etc.)
Overlay public transit lines and stations.
const transitLayer = new google.maps.TransitLayer();
transitLayer.setMap(map);Display bike routes and paths
Overlay bike-friendly paths and routes.
const bikeLayer = new google.maps.BicyclingLayer();
bikeLayer.setMap(map);Style map features based on data attributes
Apply conditional styling to GeoJSON features.
map.data.setStyle((feature) => {
return { fillColor: feature.getProperty('color') };
});Allow users to expand map to fullscreen view
Enable the standard fullscreen UI control.
<Map fullscreenControl={true} />import { APIProvider, Map, Marker, InfoWindow, useMap } from '@vis.gl/react-google-maps';
import { useEffect, useState } from 'react';
export default function MyMap() {
const [showInfo, setShowInfo] = useState(false);
const map = useMap();
useEffect(() => {
if (!map) return;
// Add layers or imperative features here
const trafficLayer = new google.maps.TrafficLayer();
trafficLayer.setMap(map);
return () => trafficLayer.setMap(null);
}, [map]);
return (
<Map
defaultCenter={{ lat: 40.7128, lng: -74.0060 }}
defaultZoom={12}
gestureHandling={'greedy'}
>
<Marker
position={{ lat: 40.7580, lng: -73.9855 }}
onClick={() => setShowInfo(true)}
/>
{showInfo && (
<InfoWindow
position={{ lat: 40.7580, lng: -73.9855 }}
onCloseClick={() => setShowInfo(false)}
>
<strong>Times Square</strong>
</InfoWindow>
)}
</Map>
);
}@vis.gl/react-google-maps,
use the useMap() hook to get the native google.maps.Map instance and initialize
the feature inside a useEffect hook.
<Marker
position={{ lat: 40.7128, lng: -74.0060 }}
icon={{
url: 'custom-icon.png',
scaledSize: { width: 32, height: 32 }
}}
/>const map = useMap();
useEffect(() => {
if (!map) return;
const polyline = new google.maps.Polyline({
path: google.maps.geometry.encoding.decodePath(encodedPath),
geodesic: true,
strokeColor: '#FF0000',
map: map
});
}, [map]);const styledMapType = new google.maps.StyledMapType([
{
elementType: 'geometry',
stylers: [{ color: '#242f3e' }]
},
{
elementType: 'labels.text.stroke',
stylers: [{ color: '#242f3e' }]
},
{
elementType: 'labels.text.fill',
stylers: [{ color: '#746855' }]
}
]);
map.mapTypes.set('styled_map', styledMapType);
map.setMapTypeId('styled_map');
Use predefined symbols (circles, squares) as marker icons
Set the icon to a Symbol object with a predefined path.
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 10,
fillColor: 'red',
fillOpacity: 1
}Create custom map elements by extending OverlayView
Extend the OverlayView class and implement onAdd, draw, and onRemove.
class CustomOverlay extends google.maps.OverlayView {
onAdd() { /* Create DOM element */ }
draw() { /* Position DOM element */ }
}Enable users to draw shapes on the map
Use DrawingManager to allow users to draw points, lines, and shapes.
new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.MARKER,
map: map
});Calculate distances, containment, and area operations
Perform spherical geometry calculations.
google.maps.geometry.spherical.computeDistanceBetween(p1, p2);
Create density visualizations from weighted point data
Render heatmaps for large point datasets.
new google.maps.visualization.HeatmapLayer({
data: points, map: map
});Style GeoJSON features dynamically based on properties
Define global or conditional styles for the data layer.
map.data.setStyle({
fillColor: 'green',
strokeWeight: 1
});Manage and cluster large numbers of markers efficiently
Group markers based on zoom level.
new MarkerClusterer(map, markers);Execute functions when marker animations complete
Listen for changes in the marker's animation state.
marker.addListener('animation_changed', () => {
if (!marker.getAnimation()) console.log('Done!');
});Convert between lat/lng and pixel coordinates
Access the current map projection for coordinate conversion.
const projection = map.getProjection();
const point = projection.fromLatLngToPoint(latLng);Position custom controls at specific corners
Place UI elements at defined map control positions.
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(btn);
Smooth zoom transitions between different zoom levels
Smoothly change the magnification level of the map.
map.setZoom(10); // Standard setZoom is smoothSmooth map movements to new locations
Smoothly shift the map center to new coordinates.
map.panTo({ lat: 40, lng: -74 });Detect and respond to user dragging interactions
Trigger actions when the user starts, continues, or stops dragging.
map.addListener('dragend', () => {
console.log('User stopped dragging');
});Show right-click menus with custom options
Listen for the right-click event to display custom UI.
map.addListener('rightclick', (e) => {
showMenuAt(e.pixel.x, e.pixel.y);
});Handle mouseover, mouseout, and other pointer events
Interact with map features via mouse movement events.
marker.addListener('mouseover', () => {
marker.setOpacity(1.0);
});Automatically fit all markers in view
Calculate the smallest bounding box containing all points.
const bounds = new google.maps.LatLngBounds();
bounds.extend(p1); bounds.extend(p2);
map.fitBounds(bounds);Prevent user interaction with the map
Disable all gestures and UI controls.
map.setOptions({ gestureHandling: 'none' });Rotate map and apply 3D tilt effects
Adjust the camera orientation for 3D views.
map.setHeading(90); map.setTilt(45);Optimize touch and mobile interactions
Define how the map responds to touch and mouse gestures.
map.setOptions({ gestureHandling: 'greedy' });Manage map attribution display
Enable or disable the display of map data sources.
map.setOptions({ mapTypeControl: true });Add text labels to marker icons
Display text characters directly on top of marker icons.
// Recommended: Use the Marker component
<Map>
<Marker
position={{ lat: 40.7128, lng: -74.0060 }}
title="New York City"
onClick={() => alert('Marker clicked!')}
/>
</Map>new google.maps.Marker({
label: 'A',
position: pos,
map: map
});Rotate marker icons for directional indication
Rotate symbols or custom icons by degrees.
icon: { path: '...', rotation: 45 }Add drop shadows to markers and overlays
Apply shadow styles or use secondary markers as shadows.
// Modern markers handle shadows via SVG filters or custom images
Set transparency levels for overlays
Adjust the visual transparency of markers and layers.
marker.setOpacity(0.5);Control layering and visibility of overlays
Specify the stack order of markers and shapes.
marker.setZIndex(100);Make polygons and polylines interactive
Enable or disable mouse click events on vector shapes.
polygon.setOptions({ clickable: true });Allow users to edit polygon and polyline vertices
Enable visual handles for users to drag and modify shapes.
polyline.setEditable(true);// Calculate distance between two points
const point1 = { lat: 40.7128, lng: -74.0060 };
const point2 = { lat: 40.7580, lng: -73.9855 };
const distance = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(point1.lat, point1.lng),
new google.maps.LatLng(point2.lat, point2.lng)
);
console.log('Distance: ' + (distance / 1000).toFixed(2) + ' km');
// Check if point is within polygon
const polygon = new google.maps.Polygon({
paths: [
{ lat: 40.7, lng: -74 },
{ lat: 40.8, lng: -74 },
{ lat: 40.8, lng: -73.9 }
]
});
const isInside = google.maps.geometry.poly.containsLocation(
new google.maps.LatLng(40.75, lng: -73.95),
polygon
);
console.log('Point inside polygon: ' + isInside);const service = new google.maps.places.PlacesService(map);
service.textSearch({
query: 'restaurants near Times Square',
location: { lat: 40.7580, lng: -73.9855 },
radius: 5000
}, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
results.forEach(place => {
console.log(place.name, place.rating);
});
}
});service.nearbySearch({
location: { lat: 40.7128, lng: -74.0060 },
radius: 2000,
type: 'restaurant',
keyword: 'pizza'
}, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
results.slice(0, 5).forEach(place => {
new google.maps.Marker({
position: place.geometry.location,
map: map,
title: place.name
});
});
}
});service.getDetails({
placeId: 'ChIJIQBpAG2dQIcR_6128GltTXQ',
fields: ['name', 'rating', 'formatted_address', 'photos',
'opening_hours', 'reviews', 'website', 'phone_number']
}, (place, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
console.log('Name:', place.name);
console.log('Rating:', place.rating);
console.log('Reviews:', place.reviews);
}
});Real-time suggestions as users type place names
Attach an autocomplete widget to a text input element.
new google.maps.places.Autocomplete(input);Get place predictions with specific types and components
Programmatically retrieve predictions without the UI widget.
service.getPlacePredictions({ input: 'Paris' }, callback);
Autocomplete for search queries, not just places
Get suggestions for broad search terms.
service.getQueryPredictions({ input: 'pizza near' }, callback);
Retrieve and display photos from Google Places
Access high-quality images associated with a place.
const url = place.photos[0].getUrl({ maxWidth: 400 });Access user reviews and ratings for places
Display snippets of user-generated reviews.
place.reviews.forEach(r => console.log(r.text));Get business hours and opening status
Check if a business is currently open.
if (place.opening_hours.isOpen()) { ... }Check if place is open, closed, or permanently closed
Verify the operational status of a business.
const status = place.business_status; // OPERATIONALFilter searches by place types (100+ categories)
Restrict search results to specific categories.
service.nearbySearch({ types: ['atm'] }, callback);Filter by price level ($, $$, $$$, $$$$)
Access price information (0 for free, 4 for expensive).
console.log('Price Level:', place.price_level);Filter places by minimum rating threshold
Filter results based on the numeric rating score.
const topRated = results.filter(p => p.rating > 4.5);Display average user ratings and review counts
Show how many people have reviewed the place.
console.log(place.user_ratings_total + ' reviews');Get official website links for places
Provide a direct link to the business's website.
const site = place.website;Access formatted and international phone numbers
Retrieve contact information for a place.
const phone = place.formatted_phone_number;Display formatted opening hours
Retrieve a list of formatted weekday strings.
place.opening_hours.weekday_text.forEach(day => ...);Calculate distance to multiple places
Determine the travel time to several nearby locations.
service.getDistanceMatrix({ origins, destinations }, callback);
UI component for address/place input
Embed a self-contained address entry field.
new google.maps.places.Autocomplete(inputElement);Limit results to specific countries or areas
Constrain suggestions to a particular geographic region.
{ componentRestrictions: { country: 'au' } }Reduce billing by grouping prediction requests
Use tokens to group user typing into a single billable event.
const token = new google.maps.places.AutocompleteSessionToken();
Load more results from search queries
Access the next set of results using the pagination object.
if (pagination.hasNextPage) pagination.nextPage();Identify permanently closed businesses
Filter out businesses that are no longer operational.
if (p.business_status === 'CLOSED_PERMANENTLY') { ... }Work with Open Location Code (Plus Code) system
Retrieve the Plus Code for a location.
const plusCode = place.plus_code.global_code;Parse addresses into street, city, zip components
Access structured address data (city, country, etc.).
place.address_components.find(c => c.types.includes('locality'));
Get results in user's preferred language
Specify the language in the API loader script.
<script src="...&language=fr"></script>Check wheelchair accessibility for places
Identify locations with accessible features.
const isAccessible = place.wheelchair_accessible_entrance;
Show recent user-submitted photos and updates
Display attribution for community-contributed content.
const attribution = photo.html_attributions[0];Access place boundaries and viewport information
Get the recommended viewport for viewing a place.
map.fitBounds(place.geometry.viewport);Bias results toward user's preferred area
Prioritize results within a specific radius or bounds.
service.textSearch({ query: 'cafe', locationBias: circle }, callback);
// Setup autocomplete with restrictions
const input = document.getElementById('autocomplete-input');
const options = {
types: ['establishment'],
componentRestrictions: { country: 'us' },
fields: ['place_id', 'geometry', 'formatted_address', 'name']
};
const autocomplete = new google.maps.places.Autocomplete(input, options);
autocomplete.bindTo('bounds', map);
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace();
if (!place.geometry) {
console.log('Place details missing');
return;
}
// Center map on selected place
map.setCenter(place.geometry.location);
map.setZoom(15);
// Add marker
new google.maps.Marker({
position: place.geometry.location,
map: map,
title: place.name
});
});const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
directionsService.route({
origin: 'New York, NY',
destination: 'Boston, MA',
waypoints: [
{ location: 'Philadelphia, PA' }
],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(result);
const route = result.routes[0];
console.log('Distance:', route.legs[0].distance.text);
console.log('Duration:', route.legs[0].duration.text);
}
});
Get multiple alternative routes and let users choose
Request alternative paths in the directions request.
service.route({ provideRouteAlternatives: true, ... }, callback);
Support driving, walking, transit, and bicycling
Specify the mode of transport for routing.
{ travelMode: google.maps.TravelMode.BICYCLING }Avoid tolls, highways, or ferries in routing
Configure routing to skip specific road features.
{ avoidTolls: true, avoidHighways: true }Add multiple stops to a route
Define intermediate locations for a route.
waypoints: [{ location: 'St. Louis, MO', stopover: true }]
Display and compare alternative routes
Iterate through the routes array in the response.
response.routes.forEach((route, i) => { ... });Get detailed turn-by-turn instructions
Access individual maneuvers within a route leg.
route.legs[0].steps.forEach(step => console.log(step.instructions));
Calculate many-to-many distances efficiently
Compute travel data for multiple origins and destinations.
service.getDistanceMatrix({ origins, destinations }, callback);
Get estimated travel times in different conditions
Retrieve the expected duration for a specific trip.
console.log(route.legs[0].duration.text);Convert encoded polylines to coordinates
Decode compressed path strings into LatLng arrays.
google.maps.geometry.encoding.decodePath(encodedString);Break routes into segments for detailed analysis
Analyze the journey between individual waypoints.
const firstLeg = response.routes[0].legs[0];Consider real-time traffic in directions
Enable traffic considerations for driving directions.
drivingOptions: { departureTime: new Date(), trafficModel: 'pessimistic' }
Display toll costs and toll roads
Check if a route contains toll-road segments.
// Identified via metadata in the directions responseManage data source attribution properly
Display mandatory legal attribution for route data.
console.log(response.routes[0].copyrights);Handle API errors and rate limiting gracefully
Check the status code returned by API services.
if (status === google.maps.DirectionsStatus.OK) { ... }Cache results to improve performance and reduce cost
Store API results in local storage or a database.
localStorage.setItem('route_cache', JSON.stringify(data));
Style route overlays with gradients and patterns
Customize the polyline appearance of a route.
polylineOptions: { strokeColor: '#FF0000', strokeWeight: 5 }
Style map elements based on data properties
Use logic to determine feature appearance.
map.data.setStyle(f => ({ icon: f.getProperty('icon_url') }));
Overlay SVG graphics on the map
Use SVG path strings for custom markers or overlays.
icon: { path: 'M 10 10 L 20 20...', scale: 1 }Use Canvas API for custom rendering
Render complex graphics directly onto a canvas element on the map.
ctx.beginPath(); ctx.arc(x, y, radius, 0, 2*Math.PI);Leverage WebGL for high-performance visuals
Use WebGLOverlayView for hardware-accelerated 2D/3D graphics.
const webgl = new google.maps.WebGLOverlayView();Create custom UI controls and panels
Push custom DOM elements into the map's control positions.
map.controls[google.maps.ControlPosition.LEFT_TOP].push(myDiv);
Add legends to explain map colors and symbols
Add a floating legend box to the map interface.
const legend = document.getElementById('legend');
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(legend);Show contextual information on hover
Display tooltips when the mouse enters a feature.
marker.addListener('mouseover', () => showTooltip(marker));
Support screen readers and keyboard navigation
Provide alternate text and keyboard focus management.
marker.setOptions({ ariaLabel: 'Hospital Location' });Optimize for touch and mobile interactions
Enable cooperative gesture handling for better scrolling.
gestureHandling: 'cooperative'Automatically adapt to device dark mode
Apply dark-themed styles based on system preferences.
if (isDarkMode) map.setOptions({ styles: darkStyles });Enable printing maps with proper styling
Configure print-specific CSS for map containers.
@media print { .map { height: 100%; width: 100%; } }Export maps as images or PDFs
Use the Static Maps API or client-side capture for exports.
// Link to Static Map: https://maps.googleapis.com/maps/api/staticmap?...
Update map data in real-time from WebSocket/MQTT
Animate markers to new positions as data arrives.
socket.on('pos', (p) => marker.setPosition(p));// Complex routing with multiple options
const origin = 'Times Square, New York';
const destination = 'Central Park, New York';
// Request multiple alternatives
directionsService.route({
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.WALKING,
provideRouteAlternatives: true,
avoidFerries: true,
avoidHighways: false,
optimizeWaypoints: true,
waypoints: [
{ location: 'Grand Central Terminal' }
]
}, (response, status) => {
if (status === google.maps.DirectionsStatus.OK) {
// Display primary route
directionsRenderer.setDirections(response);
// Analyze all routes
response.routes.forEach((route, index) => {
const leg = route.legs[0];
console.log(`Route ${index + 1}:`);
console.log(`Distance: ${leg.distance.text}`);
console.log(`Duration: ${leg.duration.text}`);
console.log(`Steps: ${leg.steps.length}`);
});
}
});Track moving objects (delivery vehicles, Uber, etc.) in real-time
Periodically update marker positions based on incoming coordinate data.
setInterval(() => marker.setPosition(newPos), 1000);Monitor and manage multiple vehicles on a single map
Maintain a collection of markers representing a fleet.
const fleet = { 'v1': marker1, 'v2': marker2 };Trigger alerts when objects enter/exit predefined areas
Check if a point is within the boundaries of a polygon.
google.maps.geometry.poly.containsLocation(point, polygon);
Visualize density patterns from location data
Analyze and display point density using HeatmapLayer.
new google.maps.visualization.HeatmapLayer({ data: points });
Overlay census and demographic data on maps
Display demographic information using themed polygons (Choropleth map).
map.data.loadGeoJson('census_data.json');Display weather conditions on specific locations
Fetch weather data and show it in info windows.
fetch(`weather_api?lat=${lat}&lon=${lng}`).then(...);Show AQI data and pollution levels
Overlay AQI values for different city regions.
// Integration with IQAir or World Air Quality Index APIsDisplay pollen forecasts and allergy information
Show regional pollen intensity levels.
// Use Google Pollen API or Ambee Pollen DataShow seismic activity and earthquake epicenters
Plot recent seismic events from the USGS API.
map.data.loadGeoJson('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson');
Display temperature, precipitation, and climate zones
Render climate trends using data layers or KML files.
new google.maps.KmlLayer({ url: 'climate_zones.kml' });Show solar potential for specific locations
Access solar insolation data for renewable energy planning.
// Use Google Solar API to calculate roof potentialDisplay active wildfires and affected areas
Plot fire perimeters using real-time satellite feeds.
// Integration with NASA FIRMS or state fire agenciesShow flood zones and flood risk levels
Overlay FEMA or local flood risk maps.
new google.maps.GroundOverlay('flood_map.png', bounds);Display property values and market data
Show property listings with price labels on markers.
new google.maps.Marker({ label: '$500k', ... });Find and display schools, hospitals, universities
Search for specific institution types using Places API.
service.nearbySearch({ type: 'school' }, callback);Visualize crime data by neighborhood
Plot incident density to identify safe/risk zones.
// Map crime incident coordinates to a HeatmapLayerDisplay real-time transit schedules and tracking
Overlay public transport routes and live vehicle locations.
new google.maps.TransitLayer().setMap(map);Find and display bike-sharing station availability
Show live bike counts at stations via GBFS (General Bikeshare Feed Specification).
fetch(station_status_url).then(...);Display available parking locations and rates
Locate parking facilities and display pricing information.
service.nearbySearch({ type: 'parking' }, callback);Locate electric vehicle charging infrastructure
Filter for charging stations and show connector types.
// Integration with Open Charge Map or Tesla Supercharger APIs
Display geotagged social media content on maps
Plot Instagram/X posts with location data on the map.
new google.maps.Marker({ icon: user_avatar, ... });Overlay historical maps and imagery
Use ImageMapType to display scanned historical maps.
map.overlayMapTypes.insertAt(0, historicalLayer);Display 3D building structures on the map
Enable 3D buildings by tilting the map camera.
map.setTilt(45); // Buildings appear in Vector maps at high zoom
Access fresh satellite imagery with refresh intervals
Toggle high-resolution satellite/hybrid views.
map.setMapTypeId(google.maps.MapTypeId.HYBRID);Show terrain features and elevation profiles
Query ground elevation for specific coordinates.
new google.maps.ElevationService().getElevationForLocations(...);
Display national parks and protected wildlife areas
Identify park boundaries and facilities.
service.nearbySearch({ type: 'park' }, callback);Overlay zoning and land use data
Style regions based on land classification properties.
map.data.setStyle(f => ({ fillColor: getColor(f.getProperty('zone')) }));
Visualize utility networks and infrastructure
Draw complex infrastructure networks using polylines and markers.
const fiberLine = new google.maps.Polyline({ path, color: 'blue' });
Connect to PostgreSQL, MongoDB, Firebase for dynamic data
Sync map state with a backend spatial database.
fetch('/api/v1/markers').then(r => r.json()).then(showMarkers);
Query map data efficiently using GraphQL APIs
Retrieve only the necessary spatial fields via GraphQL.
const { data } = await client.query({ query: GET_MAP_DATA });
useState to manage dynamic data (like markers
or paths) and pass them as props to the map components. This ensures your map stays in sync with your
application state.
// Lazy load map with Intersection Observer
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
initMap();
observer.unobserve(entry.target);
}
});
});
observer.observe(document.getElementById('map'));// Include markerclusterer library
const clusterer = new MarkerClusterer(map, markers, {
maxZoom: 15,
gridSize: 60,
imagePath: 'images/m'
});// Backend proxy pattern (Node.js/Express)
app.get('/api/directions', async (req, res) => {
const { origin, destination } = req.query;
const result = await directionsService(origin, destination);
res.json(result);
});
// Frontend - call your proxy, not Google API directly
fetch('/api/directions?origin=...&destination=...')
.then(r => r.json())
.then(data => console.log(data));// Responsive map initialization
const map = new google.maps.Map(document.getElementById('map'), {
center: defaultCenter,
zoom: window.innerWidth < 768 ? 12 : 13,
gestureHandling: 'greedy', // Better mobile experience
fullscreenControl: true,
zoomControl: true,
streetViewControl: window.innerWidth > 768
});// Schema markup for maps
// Enable debug mode
google.maps.MapTypeId.DEBUG = true;
// Log all events
map.addListener('idle', () => console.log('Map idle'));
map.addListener('center_changed', () => console.log('Center:', map.getCenter()));
map.addListener('zoom_changed', () => console.log('Zoom:', map.getZoom()));Endpoint: api.openweathermap.org/data/2.5/weather
Features: Current weather, forecasts, air quality
// Get weather for map location
async function getWeather(lat, lng) {
const apiKey = 'YOUR_OPENWEATHER_KEY';
const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lng}&appid=${apiKey}&units=metric`;
const response = await fetch(url);
const data = await response.json();
return {
temp: data.main.temp,
description: data.weather[0].description,
humidity: data.main.humidity,
windSpeed: data.wind.speed,
icon: data.weather[0].icon
};
}
// Display weather on map
map.addListener('idle', async () => {
const center = map.getCenter();
const weather = await getWeather(center.lat(), center.lng());
console.log(`${weather.temp}Β°C - ${weather.description}`);
});Coverage: USA only, highly accurate
Cost: Free, no API key required
// NOAA Weather API (USA)
async function getNOAAWeather(lat, lng) {
// First get grid point
const gridUrl = `https://api.weather.gov/points/${lat},${lng}`;
const gridRes = await fetch(gridUrl);
const gridData = await gridRes.json();
// Then get forecast
const forecastUrl = gridData.properties.forecast;
const forecastRes = await fetch(forecastUrl);
const forecast = await forecastRes.json();
return forecast.properties.periods;
}
getNOAAWeather(40.7128, -74.0060).then(periods => {
periods.slice(0, 3).forEach(period => {
console.log(`${period.name}: ${period.temperature}Β°${period.temperatureUnit}`);
});
});Coverage: Global air quality data
Features: AQI, pollutant levels, health recommendations
// Get Air Quality Index
async function getAirQuality(lat, lng) {
const apiKey = 'YOUR_IQAIR_KEY';
const url = `https://api.waqi.info/feed/geo:${lat};${lng}/?token=${apiKey}`;
const response = await fetch(url);
const data = await response.json();
if (data.status === 'ok') {
const aqi = data.data.aqi;
let quality = 'Unknown';
if (aqi <= 50) quality = 'Good';
else if (aqi <= 100) quality = 'Moderate';
else if (aqi <= 150) quality = 'Unhealthy for Sensitive Groups';
else if (aqi <= 200) quality = 'Unhealthy';
else if (aqi <= 300) quality = 'Very Unhealthy';
else quality = 'Hazardous';
return { aqi, quality, city: data.data.city.name };
}
}
// Add AQI info window to map
getAirQuality(40.7128, -74.0060).then(data => {
const infoWindow = new google.maps.InfoWindow({
content: `<strong>${data.city}</strong><br>AQI: ${data.aqi} (${data.quality})`
});
infoWindow.open(map);
});New Allergen forecast data
Coverage: USA, Europe, and expanding
// Get Pollen Forecast (requires Maps SDK v1.47+)
async function getPollenForecast(lat, lng) {
const location = { latitude: lat, longitude: lng };
// Use Google Pollen API
const pollenMap = new google.maps.pollen.PollenetMap();
pollenMap.addListener('click', (event) => {
const pollenData = event.getPollenInfo();
console.log('Pollen Index:', pollenData.pollenIndex);
console.log('Allergens:', pollenData.allergens);
});
}
// Display pollen info
const allergenInfo = {
'tree': 'Tree Pollen',
'grass': 'Grass Pollen',
'weed': 'Weed Pollen'
};
const forecastData = {
tree: 'Low',
grass: 'High',
weed: 'Moderate'
};
let content = '<strong>Pollen Forecast</strong><br>';
Object.entries(forecastData).forEach(([key, level]) => {
content += `${allergenInfo[key]}: ${level}<br>`;
});
const infoWindow = new google.maps.InfoWindow({ content });
infoWindow.open(map);Global Coverage: Worldwide pollen tracking
Features: Pollen index, risk levels, recommendations
// Ambee Pollen API
async function getAmbeePollen(lat, lng) {
const apiKey = 'YOUR_AMBEE_KEY';
const url = `https://api.ambeedata.com/latest/pollen?lat=${lat}&lng=${lng}`;
const response = await fetch(url, {
headers: { 'x-api-key': apiKey }
});
const data = await response.json();
const result = data.data.riskLevel;
return {
riskLevel: result.plantType,
risk: result.risk,
concentration: result.concentration
};
}
// Create pollen layer on map
async function createPollenLayer(lat, lng) {
const pollen = await getAmbeePollen(lat, lng);
const color = pollen.risk > 300 ? 'red' :
pollen.risk > 150 ? 'orange' :
pollen.risk > 50 ? 'yellow' : 'green';
new google.maps.Circle({
center: { lat, lng },
radius: 5000,
fillColor: color,
fillOpacity: 0.3,
strokeColor: color,
strokeOpacity: 0.8,
map: map
});
}| API Name | Purpose | Key Feature | Pricing |
|---|---|---|---|
| TimeZoneDB | Timezone information | Get timezone for any coordinate | Free tier available |
| OpenStreetMap | Alternative map tiles | Open-source, customizable | Free |
| Here Maps API | Routing alternative | Advanced routing, traffic | Paid |
| Mapbox | Custom mapping | Custom styles, vector tiles | Free + paid tiers |
| Overpass API | OSM data queries | Query OSM database | Free |
| NASA FIRMS | Fire/hotspot detection | Real-time satellite data | Free |
Visit Google Maps JS Samples on GitHub for official code examples.