Isomorphic React component for rendering custom React components on Google Maps with full server-side rendering support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
React components rendered as children of GoogleMapReact are automatically positioned on the map using lat/lng coordinates. The component provides an internal hover system and passes special props to child components.
Required and automatic props for components rendered on the map.
/**
* Props interface for components rendered as children of GoogleMapReact
*/
interface MapChildProps {
// Required positioning props
lat: number;
lng: number;
// Automatically injected props
$hover?: boolean;
// Custom props (any additional props you want to pass)
[key: string]: any;
}Basic Usage:
// Simple marker component
const Marker = ({ text, $hover }) => (
<div style={{
backgroundColor: $hover ? 'red' : 'blue',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
transform: 'translate(-50%, -50%)' // Center the marker
}}>
{text}
</div>
);
// Use in map
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
>
<Marker
lat={37.7749}
lng={-122.4194}
text="San Francisco"
/>
</GoogleMapReact>GoogleMapReact includes an internal hover detection system that automatically manages hover states for child components.
interface HoverSystemProps {
// Distance function for hover detection
distanceToMouse?: (
pt: { x: number; y: number },
mousePos: { x: number; y: number },
markerProps: any
) => number;
// Maximum distance for hover detection (pixels)
hoverDistance?: number;
// Whether to debounce hover calculations
debounced?: boolean;
}Advanced Hover Example:
const InteractiveMarker = ({ text, $hover, onClick }) => (
<div
style={{
backgroundColor: $hover ? '#ff4444' : '#4444ff',
color: 'white',
padding: '8px 12px',
borderRadius: '8px',
cursor: 'pointer',
transform: 'translate(-50%, -50%)',
transition: 'all 0.2s ease',
boxShadow: $hover ? '0 4px 8px rgba(0,0,0,0.3)' : '0 2px 4px rgba(0,0,0,0.2)',
fontSize: $hover ? '14px' : '12px'
}}
onClick={onClick}
>
{text}
</div>
);
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
hoverDistance={20}
debounced={true}
onChildClick={(childKey, childProps) => {
console.log('Marker clicked:', childProps.text);
}}
>
<InteractiveMarker
key="sf-marker"
lat={37.7749}
lng={-122.4194}
text="Click me!"
onClick={() => alert('Marker clicked!')}
/>
</GoogleMapReact>Guidelines for properly positioning child components on the map.
interface PositioningStyles {
position?: 'absolute' | 'relative';
transform?: string;
transformOrigin?: string;
left?: string | number;
top?: string | number;
}Positioning Examples:
// Centered marker (most common)
const CenteredMarker = ({ children }) => (
<div style={{
position: 'absolute',
transform: 'translate(-50%, -50%)',
transformOrigin: 'center center'
}}>
{children}
</div>
);
// Bottom-centered marker (pin style)
const PinMarker = ({ children }) => (
<div style={{
position: 'absolute',
transform: 'translate(-50%, -100%)',
transformOrigin: 'center bottom'
}}>
{children}
</div>
);
// Top-left positioned marker
const TopLeftMarker = ({ children }) => (
<div style={{
position: 'absolute',
transform: 'translate(0, 0)',
transformOrigin: 'top left'
}}>
{children}
</div>
);
// Usage
<GoogleMapReact defaultCenter={{ lat: 37.7749, lng: -122.4194 }} defaultZoom={10}>
<CenteredMarker lat={37.7749} lng={-122.4194}>
<div style={{ backgroundColor: 'red', width: 20, height: 20, borderRadius: '50%' }} />
</CenteredMarker>
<PinMarker lat={37.7849} lng={-122.4094}>
<div style={{ backgroundColor: 'blue', width: 0, height: 0, borderLeft: '10px solid transparent', borderRight: '10px solid transparent', borderTop: '20px solid blue' }} />
</PinMarker>
</GoogleMapReact>Create markers dynamically from data arrays.
function DynamicMarkers({ locations }) {
return (
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
>
{locations.map((location, index) => (
<Marker
key={location.id || index}
lat={location.lat}
lng={location.lng}
text={location.name}
type={location.type}
/>
))}
</GoogleMapReact>
);
}
// Usage
const locations = [
{ id: 1, lat: 37.7749, lng: -122.4194, name: 'San Francisco', type: 'city' },
{ id: 2, lat: 37.7849, lng: -122.4094, name: 'North Beach', type: 'neighborhood' },
{ id: 3, lat: 37.7649, lng: -122.4294, name: 'Mission', type: 'neighborhood' }
];
<DynamicMarkers locations={locations} />Show/hide markers based on zoom level or other conditions.
function ZoomDependentMarkers({ zoom, markers }) {
return (
<GoogleMapReact
zoom={zoom}
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
onChange={({ zoom }) => setZoom(zoom)}
>
{markers.map(marker => {
// Only show detailed markers at high zoom levels
if (marker.type === 'detailed' && zoom < 12) {
return null;
}
// Only show overview markers at low zoom levels
if (marker.type === 'overview' && zoom > 8) {
return null;
}
return (
<Marker
key={marker.id}
lat={marker.lat}
lng={marker.lng}
text={marker.name}
/>
);
})}
</GoogleMapReact>
);
}Implement custom hover detection logic.
function CustomHoverMap() {
const customDistanceFunction = (pt, mousePos, markerProps) => {
// Custom distance calculation - larger hover area for important markers
const baseDistance = Math.sqrt(
Math.pow(pt.x - mousePos.x, 2) + Math.pow(pt.y - mousePos.y, 2)
);
// Increase hover distance for VIP markers
return markerProps.isVIP ? baseDistance * 0.5 : baseDistance;
};
return (
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
distanceToMouse={customDistanceFunction}
hoverDistance={30}
>
<Marker lat={37.7749} lng={-122.4194} text="VIP Location" isVIP={true} />
<Marker lat={37.7849} lng={-122.4094} text="Regular Location" isVIP={false} />
</GoogleMapReact>
);
}Handle various mouse events on child components.
function EventHandlingMap() {
const handleChildMouseEnter = (childKey, childProps) => {
console.log('Mouse entered:', childProps.text);
};
const handleChildMouseLeave = (childKey, childProps) => {
console.log('Mouse left:', childProps.text);
};
const handleChildClick = (childKey, childProps) => {
console.log('Clicked:', childProps.text);
};
const handleChildMouseDown = (childKey, childProps) => {
console.log('Mouse down:', childProps.text);
};
const handleChildMouseUp = (childKey, childProps) => {
console.log('Mouse up:', childProps.text);
};
return (
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
onChildMouseEnter={handleChildMouseEnter}
onChildMouseLeave={handleChildMouseLeave}
onChildClick={handleChildClick}
onChildMouseDown={handleChildMouseDown}
onChildMouseUp={handleChildMouseUp}
>
<InteractiveMarker
lat={37.7749}
lng={-122.4194}
text="Interactive Marker"
/>
</GoogleMapReact>
);
}Use React.memo and stable keys to optimize performance with many markers.
const Marker = React.memo(({ lat, lng, text, $hover }) => (
<div style={{
backgroundColor: $hover ? 'red' : 'blue',
color: 'white',
padding: '4px 8px',
borderRadius: '4px',
transform: 'translate(-50%, -50%)'
}}>
{text}
</div>
));
// Use stable keys and avoid inline object creation
function OptimizedMarkers({ locations }) {
return (
<GoogleMapReact defaultCenter={{ lat: 37.7749, lng: -122.4194 }} defaultZoom={10}>
{locations.map(location => (
<Marker
key={location.id} // Stable key
lat={location.lat}
lng={location.lng}
text={location.name}
/>
))}
</GoogleMapReact>
);
}