Migrating from v2 to v3
Version 3.0 of @react-google-maps/api introduces several breaking changes and improvements. This guide will help you migrate your application smoothly.
Overview of Changes
v3.0 represents a major shift in the library’s architecture and licensing:
- Commercial License: v3.x is commercial software (v2.x remains open-source)
- Functional Components Only: All class-based components removed
- Improved Performance: Better memory management and re-render optimization
- Updated Dependencies: React 19 support, TypeScript 5.9.3
- Simplified Build: Removed Lerna, ESLint config, Storybook
Breaking Changes
Make sure to test thoroughly in a development environment before upgrading production applications.
1. Class Components Removed
All class-based component variants have been removed. Only functional (hooks-based) components are supported.
v2.x:
import { Marker, MarkerF } from '@react-google-maps/api';
// Both class and functional variants available
<Marker position={position} /> // Class component
<MarkerF position={position} /> // Functional component
v3.x:
import { Marker } from '@react-google-maps/api';
// Only functional components (the 'F' suffix is now the default)
<Marker position={position} />
Find all component imports
Search your codebase for imports from @react-google-maps/api
Remove 'F' suffix from imports
Change MarkerF, PolylineF, etc. to Marker, Polyline, etc.
Test component behavior
Functional components use hooks, so verify lifecycle behavior matches your expectations
2. Component API Changes
All components now use the functional API exclusively:
Before (v2.x):
import { Circle } from '@react-google-maps/api';
class MapWithCircle extends React.Component {
render() {
return (
<GoogleMap>
<Circle
center={{ lat: 40.7128, lng: -74.006 }}
radius={1000}
/>
</GoogleMap>
);
}
}
After (v3.x):
import { Circle } from '@react-google-maps/api';
import { useMemo } from 'react';
function MapWithCircle() {
const center = useMemo(() => ({ lat: 40.7128, lng: -74.006 }), []);
return (
<GoogleMap>
<Circle center={center} radius={1000} />
</GoogleMap>
);
}
Use useMemo for object props (like center, position) and useCallback for function props to prevent unnecessary re-renders.
3. useJsApiLoader is Now Preferred
While LoadScript still works, useJsApiLoader is now the recommended approach:
Before (v2.x):
import { LoadScript, GoogleMap } from '@react-google-maps/api';
function App() {
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<GoogleMap />
</LoadScript>
);
}
After (v3.x - Recommended):
import { useJsApiLoader, GoogleMap } from '@react-google-maps/api';
import { useMemo } from 'react';
const libraries = ['places']; // Define outside component or use useMemo
function App() {
const { isLoaded, loadError } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries,
});
if (loadError) return <div>Error loading maps</div>;
if (!isLoaded) return <div>Loading...</div>;
return <GoogleMap />;
}
4. Libraries Array Must Be Stable
The libraries prop must now be a stable reference to avoid re-loading the API:
Incorrect (causes re-renders):
function App() {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries: ['places', 'drawing'], // ❌ New array on each render
});
}
Correct:
// Option 1: Define outside component
const libraries = ['places', 'drawing'];
function App() {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries, // ✅ Stable reference
});
}
// Option 2: Use useMemo
function App() {
const libraries = useMemo(() => ['places', 'drawing'], []);
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries, // ✅ Stable reference
});
}
5. TypeScript Types Updated
TypeScript support has been improved with updated type definitions:
v2.x:
import { Marker, MarkerF } from '@react-google-maps/api';
import type { MarkerProps } from '@react-google-maps/api';
v3.x:
import { Marker, type MarkerProps } from '@react-google-maps/api';
License Changes
Version 3.x is commercial software and requires a license for commercial use.
Key licensing points:
If you need to continue using the open-source version, stay on v2.x.
Migration Checklist
Review license requirements
Ensure you have a commercial license for v3.x or plan to stay on v2.x
Update package version
npm install @react-google-maps/api@3
Remove 'F' suffix from all imports
Search and replace MarkerF → Marker, PolylineF → Polyline, etc.
Convert class components to functional
If you were using class-based components, refactor to functional components with hooks
Stabilize libraries array
Move libraries array outside component or wrap in useMemo
Add useMemo/useCallback
Wrap object props in useMemo and function props in useCallback
Update TypeScript imports
Use import type for type-only imports
Test thoroughly
Verify all map features work correctly, especially:
- Event handlers (
onClick, onDrag, etc.)
- Marker clustering
- Custom overlays
- Autocomplete and Places API features
Example Migration
Here’s a complete before/after example:
Before (v2.x)
import React from 'react';
import {
LoadScript,
GoogleMap,
MarkerF,
InfoWindowF,
} from '@react-google-maps/api';
class MyMap extends React.Component {
state = {
selectedMarker: null,
};
handleMarkerClick = (marker) => {
this.setState({ selectedMarker: marker });
};
render() {
return (
<LoadScript googleMapsApiKey="YOUR_API_KEY" libraries={['places']}>
<GoogleMap
mapContainerStyle={{ width: '100%', height: '400px' }}
center={{ lat: 40.7128, lng: -74.006 }}
zoom={12}
>
<MarkerF
position={{ lat: 40.7128, lng: -74.006 }}
onClick={() => this.handleMarkerClick({ lat: 40.7128, lng: -74.006 })}
/>
{this.state.selectedMarker && (
<InfoWindowF
position={this.state.selectedMarker}
onCloseClick={() => this.setState({ selectedMarker: null })}
>
<div>Selected Location</div>
</InfoWindowF>
)}
</GoogleMap>
</LoadScript>
);
}
}
After (v3.x)
import { useMemo, useCallback, useState } from 'react';
import {
useJsApiLoader,
GoogleMap,
Marker,
InfoWindow,
} from '@react-google-maps/api';
const libraries = ['places'];
function MyMap() {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries,
});
const [selectedMarker, setSelectedMarker] = useState(null);
const containerStyle = useMemo(
() => ({ width: '100%', height: '400px' }),
[]
);
const center = useMemo(
() => ({ lat: 40.7128, lng: -74.006 }),
[]
);
const handleMarkerClick = useCallback((position) => {
setSelectedMarker(position);
}, []);
const handleCloseClick = useCallback(() => {
setSelectedMarker(null);
}, []);
if (!isLoaded) return <div>Loading...</div>;
return (
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={12}
>
<Marker
position={center}
onClick={() => handleMarkerClick(center)}
/>
{selectedMarker && (
<InfoWindow
position={selectedMarker}
onCloseClick={handleCloseClick}
>
<div>Selected Location</div>
</InfoWindow>
)}
</GoogleMap>
);
}
Common Issues
Problem: Map re-renders excessively after upgrading.
Solution: Ensure all object and function props are memoized:
// ❌ Bad
function Map() {
return (
<GoogleMap
center={{ lat: 40, lng: -74 }} // New object each render
onClick={(e) => console.log(e)} // New function each render
/>
);
}
// ✅ Good
function Map() {
const center = useMemo(() => ({ lat: 40, lng: -74 }), []);
const handleClick = useCallback((e) => console.log(e), []);
return <GoogleMap center={center} onClick={handleClick} />;
}
Issue: Libraries Reload Warning
Problem: Console warning about libraries reloading.
Solution: Move libraries outside component:
const libraries = ['places'];
function App() {
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: 'YOUR_API_KEY',
libraries, // Use stable reference
});
}
Issue: Marker Clustering Broken
Problem: Markers don’t cluster after upgrade.
Solution: Update to use the new clusterer API:
import { MarkerClusterer } from '@react-google-maps/api';
function Map() {
const markers = useMemo(() => [
{ lat: 40.7128, lng: -74.006 },
{ lat: 40.7580, lng: -73.9855 },
], []);
return (
<GoogleMap>
<MarkerClusterer>
{(clusterer) =>
markers.map((position, i) => (
<Marker key={i} position={position} clusterer={clusterer} />
))
}
</MarkerClusterer>
</GoogleMap>
);
}
Getting Help
If you encounter issues during migration:
Next Steps
After migrating, explore new v3 features: