Skip to content

Instantly share code, notes, and snippets.

@ydrea
Created March 27, 2025 11:33
Show Gist options
  • Save ydrea/27f70d9318981730be93948e2a54cb63 to your computer and use it in GitHub Desktop.
Save ydrea/27f70d9318981730be93948e2a54cb63 to your computer and use it in GitHub Desktop.
react-native-maps + @teovilla/react-native-web-maps
import React from 'react';
import { Platform, StyleProp, ViewStyle } from 'react-native';
// Native types
import type {
MapViewProps as NativeMapViewProps,
Marker as NativeMarker,
Region,
LatLng,
} from 'react-native-maps';
// Web types (manually defined)
type WebMapProps = {
style?: ViewStyle;
center: { lat: number; lng: number };
zoom: number;
googleMapsApiKey?: string;
onViewportChanged?: (viewport: WebViewport) => void;
children?: React.ReactNode;
};
type WebMarkerProps = {
lat: number;
lng: number;
title?: string;
children?: React.ReactNode;
[key: string]: any; // Allow additional web-specific props
};
type WebViewport = {
center: { lat: number; lng: number };
zoom: number;
};
// Configuration type
type MapConfiguration = {
googleMapsApiKey?: string; // Required for web and Android
androidGoogleMapsApiKey?: string; // Android-specific key
webGoogleMapsApiKey?: string; // Web-specific key
};
// Context for API keys
const MapConfigContext = React.createContext<MapConfiguration>({});
// Provider component
export const MapConfigProvider: React.FC<MapConfiguration & { children: React.ReactNode }> = ({
children,
...config
}) => (
<MapConfigContext.Provider value={config}>
{children}
</MapConfigContext.Provider>
);
// Main MapView component
const MapViewComponent: React.FC<
Omit<NativeMapViewProps, 'provider'> & {
style?: StyleProp<ViewStyle>;
web?: Omit<WebMapProps, 'center' | 'zoom' | 'style'>;
native?: Omit<NativeMapViewProps, 'region' | 'style'>;
}
> = (props) => {
const config = React.useContext(MapConfigContext);
const { style, region, children, web, native, ...rest } = props;
if (Platform.OS === 'web') {
const WebMap = require('@teovilla/react-native-web-maps').default;
const apiKey = config.webGoogleMapsApiKey || config.googleMapsApiKey;
if (!apiKey) {
console.error('Google Maps API key is required for web');
return null;
}
return (
<WebMap
style={style as ViewStyle}
center={{
lat: region?.latitude || 0,
lng: region?.longitude || 0,
}}
zoom={calculateZoom(region?.latitudeDelta)}
googleMapsApiKey={apiKey}
{...web}
>
{children}
</WebMap>
);
}
const NativeMap = require('react-native-maps').default;
// Android requires API key
if (Platform.OS === 'android' && !config.androidGoogleMapsApiKey && !config.googleMapsApiKey) {
console.error('Google Maps API key is required for Android');
return null;
}
return (
<NativeMap
style={style as ViewStyle}
region={region}
{...(Platform.OS === 'android' ? {
googleMapsApiKey: config.androidGoogleMapsApiKey || config.googleMapsApiKey
} : {})}
{...native}
{...rest}
>
{children}
</NativeMap>
);
};
// Marker Component Implementation
const MarkerComponent: React.FC<{
coordinate: LatLng;
title?: string;
description?: string;
children?: React.ReactNode;
web?: Omit<WebMarkerProps, 'lat' | 'lng'>;
native?: Omit<React.ComponentProps<typeof NativeMarker>, 'coordinate'>;
}> = (props) => {
const { coordinate, title, description, children, web, native } = props;
if (Platform.OS === 'web') {
const WebMarker = require('@teovilla/react-native-web-maps').Marker;
return (
<WebMarker
lat={coordinate.latitude}
lng={coordinate.longitude}
title={title}
{...web}
>
{children}
</WebMarker>
);
}
const NativeMarker = require('react-native-maps').Marker;
return (
<NativeMarker
coordinate={coordinate}
title={title}
description={description}
{...native}
>
{children}
</NativeMarker>
);
};
// Helper function for zoom calculation
const calculateZoom = (latDelta?: number): number => {
return latDelta ? Math.log2(360 / latDelta) : 10;
};
// Memoized components
export const MapView = React.memo(MapViewComponent);
export const Marker = React.memo(MarkerComponent);
@ydrea
Copy link
Author

ydrea commented Mar 27, 2025

use:

import { MapConfigProvider, MapView, Marker } from './unimap';

const App = () => (
  <MapConfigProvider
    googleMapsApiKey="YOUR_SHARED_KEY" // Fallback for both platforms
    webGoogleMapsApiKey="YOUR_WEB_KEY"  // Web-specific key
    androidGoogleMapsApiKey="YOUR_ANDROID_KEY" // Android-specific key
  >
    <MapView
      style={{ flex: 1 }}
      initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.0922,
        longitudeDelta: 0.0421,
      }}
    >
      <Marker
        coordinate={{ latitude: 37.78825, longitude: -122.4324 }}
        title="San Francisco"
        description="Golden Gate Bridge"
      />
    </MapView>
  </MapConfigProvider>
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment