A maintained React 19 wrapper for Google Maps with declarative maps, markers, advanced markers, clustering, overlays, directions, geocoding hooks, and versioned live demos.
Documentation & Live Demos | npm | Issues | Repository
Latest version: 19.0.0
@stackline/react-google-maps is built for teams that want the same clarity people liked in the Angular Google Maps package, but in React:
- a single provider that loads the Google Maps JavaScript API once
- a declarative
<GoogleMap>shell with familiar props likecenter,zoom,mapId, andoptions - component wrappers for markers, advanced markers, info windows, overlays, layers, controls, and directions
- hooks for service-style flows like geocoding
- official marker clustering support through
@googlemaps/markerclusterer
The result is intentionally migration-friendly. A complex Angular or imperative Google Maps codebase can usually move over one feature at a time without giving up access to the native map instances.
This package is not a "locked box" wrapper. The components are meant to stay declarative for common usage, while still giving you the same escape hatches teams rely on in Angular:
refhandles onGoogleMap,MapMarker,MapInfoWindow,MapMarkerClusterer,MapPolyline,MapPolygon,MapRectangle,MapCircle,MapGroundOverlay, andMapDirectionsRendereronLoadcallbacks that expose the native Google Maps instances directlyuseGoogleMap()to access the current nativegoogle.maps.Mapfrom nested React componentsuseMapGeocoder()anduseDirectionsService()for service-style integrations
That means you can keep a typed React API for the 90% case and still drop to native Google Maps methods when a complex application needs something custom.
Each package family only installs on its matching React family. Framework major and package major are not always the same package number, so use the package family column below.
| Package family | Framework family | Peer range | Tested release window | Demo link |
|---|---|---|---|---|
| 19.x | React 19 only | >=19.0.0 <20.0.0 |
19.0.0 -> 19.2.5 | React 19 family docs |
| 18.x | React 18 only | >=18.0.0 <19.0.0 |
18.0.0 -> 18.3.1 | React 18 family docs |
| 17.x | React 17 only | >=17.0.0 <18.0.0 |
17.0.0 -> 17.0.2 | React 17 family docs |
npm install @stackline/react-google-mapsChoose the package family from the compatibility table above. Each published family is locked to one framework major only.
import {
GoogleMap,
GoogleMapsProvider,
MapMarker
} from '@stackline/react-google-maps';
const center = { lat: 40.7128, lng: -74.006 };
export function App() {
return (
<GoogleMapsProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY} mapIds={['DEMO_MAP_ID']}>
<GoogleMap center={center} zoom={11} height={420}>
<MapMarker position={center} title="New York City" />
</GoogleMap>
</GoogleMapsProvider>
);
}import {
GoogleMap,
GoogleMapsProvider,
MapAdvancedMarker
} from '@stackline/react-google-maps';
export function CapitalMap() {
return (
<GoogleMapsProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY} mapIds={['DEMO_MAP_ID']}>
<GoogleMap center={{ lat: 45.4215, lng: -75.6972 }} zoom={6} mapId="DEMO_MAP_ID" height={420}>
<MapAdvancedMarker position={{ lat: 45.4215, lng: -75.6972 }}>
<div style={{ padding: 12, borderRadius: 12, background: 'white' }}>
<strong>Ottawa</strong>
<div>Advanced marker content</div>
</div>
</MapAdvancedMarker>
</GoogleMap>
</GoogleMapsProvider>
);
}import {
createClusterRenderer,
GoogleMap,
GoogleMapsProvider,
MapAdvancedMarker,
MapMarker,
MapMarkerClusterer
} from '@stackline/react-google-maps';
const points = [
{ lat: 37.782, lng: -122.447 },
{ lat: 37.789, lng: -122.405 },
{ lat: 37.766, lng: -122.438 }
];
export function ClusteredMap() {
return (
<GoogleMapsProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY}>
<GoogleMap center={{ lat: 37.7749, lng: -122.4194 }} zoom={10} height={420}>
<MapMarkerClusterer>
{points.map((point, index) => (
<MapMarker key={index} position={point} />
))}
</MapMarkerClusterer>
</GoogleMap>
</GoogleMapsProvider>
);
}If you need branded cluster visuals, the wrapper also exposes a helper for HTML or advanced-marker-based cluster rendering:
const renderer = createClusterRenderer({
render: ({ count }) => {
const element = document.createElement('div');
element.className = 'cluster-card';
element.innerHTML = `<strong>${count}</strong><span>places</span>`;
return element;
}
});
<MapMarkerClusterer renderer={renderer}>
<MapAdvancedMarker position={point}>...</MapAdvancedMarker>
</MapMarkerClusterer>import { useState } from 'react';
import {
GoogleMap,
GoogleMapsProvider,
MapDirectionsRenderer,
MapDirectionsService
} from '@stackline/react-google-maps';
export function DirectionsDemo() {
const [directions, setDirections] = useState<google.maps.DirectionsResult | null>(null);
return (
<GoogleMapsProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY}>
<GoogleMap center={{ lat: 45.4215, lng: -75.6972 }} zoom={6} height={420}>
<MapDirectionsService
request={{
origin: 'Toronto, ON',
destination: 'Montreal, QC',
travelMode: 'DRIVING' as google.maps.TravelMode
}}
onResult={({ result }) => setDirections(result ?? null)}
/>
<MapDirectionsRenderer directions={directions} />
</GoogleMap>
</GoogleMapsProvider>
);
}import { useState } from 'react';
import {
GoogleMap,
GoogleMapsProvider,
MapMarker,
useMapGeocoder
} from '@stackline/react-google-maps';
function GeocoderInner() {
const geocoder = useMapGeocoder();
const [center, setCenter] = useState({ lat: 43.6532, lng: -79.3832 });
async function geocodeAddress() {
const response = await geocoder?.geocode({ address: 'Toronto City Hall' });
const first = response?.results[0];
if (first?.geometry.location) {
setCenter(first.geometry.location.toJSON());
}
}
return (
<>
<button onClick={geocodeAddress}>Geocode</button>
<GoogleMap center={center} zoom={14} height={420}>
<MapMarker position={center} />
</GoogleMap>
</>
);
}
export function GeocoderDemo() {
return (
<GoogleMapsProvider apiKey={import.meta.env.VITE_GOOGLE_MAPS_API_KEY}>
<GeocoderInner />
</GoogleMapsProvider>
);
}| Surface | Notes |
|---|---|
GoogleMapsProvider |
Loads the API once and shares readiness through context. |
GoogleMap |
Creates the native google.maps.Map and supports refs for imperative access. |
MapMarker |
Thin wrapper around google.maps.Marker. |
MapAdvancedMarker |
Thin wrapper around google.maps.marker.AdvancedMarkerElement. |
MapInfoWindow |
Declarative info window with React content via portal. |
MapMarkerClusterer |
Official clustering through @googlemaps/markerclusterer. |
MapPolyline, MapPolygon, MapRectangle, MapCircle |
Declarative shape overlays. |
MapGroundOverlay, MapKmlLayer, MapHeatmapLayer |
Common overlays and layers. |
MapTrafficLayer, MapTransitLayer, MapBicyclingLayer |
Built-in transportation layers. |
MapDirectionsService, MapDirectionsRenderer |
Declarative routing request + display flow. |
useMapGeocoder() |
Memoized geocoder hook. |
useDirectionsService() |
Memoized directions service hook. |
useGoogleMap() |
Access the native google.maps.Map from nested components. |
MapControl |
Mount React UI inside native map control positions. |
The live docs are intentionally rich and mirror the kinds of workflows developers look for in the official Google Maps documentation:
- basic map bootstrapping
- controlled center and zoom
- click events
- markers and info windows
- advanced markers
- draggable markers
- marker clustering
- polylines, polygons, rectangles, circles
- ground overlays
- traffic, transit, and bicycling layers
- KML layers
- heatmaps
- directions
- geocoding
- custom map controls
- Initial React 19 line
- Added versioned docs for React 17, 18, and 19
- Added declarative wrappers for maps, markers, advanced markers, clustering, shapes, layers, directions, and geocoding
- React 18 compatibility line
- React 17 compatibility line
Report vulnerabilities privately by following SECURITY.md. Do not disclose exploit details in a public issue.
Licensed under the MIT License. See LICENSE.