npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@silicon.js/map

v1.0.4

Published

A React Native map management package built on top of `react-native-maps` for Expo with a powerful provider-based architecture for managing regions, markers, and selections.

Readme

Map Package

A React Native map management package built on top of react-native-maps for Expo with a powerful provider-based architecture for managing regions, markers, and selections.

Features

  • 🗺️ Easy map region management
  • 📍 Full marker CRUD operations (Create, Read, Update, Delete)
  • 🎯 Marker selection with visual feedback
  • 📱 User location tracking
  • 🎨 Custom marker icons support
  • 🔄 Automatic state synchronization
  • 💾 Centralized map state management
  • 🎭 TypeScript support
  • ⚡ Optimized with memoized callbacks
  • 📦 Expo-compatible

Installation

npx expo install react-native-maps

Setup

1. Configure app.json/app.config.js

Add the maps plugin to your Expo config:

{
  "expo": {
    "plugins": [
      [
        "expo-location",
        {
          "locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
        }
      ]
    ],
    "ios": {
      "bundleIdentifier": "com.yourcompany.yourapp",
      "config": {
        "googleMapsApiKey": "YOUR_IOS_API_KEY"
      }
    },
    "android": {
      "package": "com.yourcompany.yourapp",
      "config": {
        "googleMaps": {
          "apiKey": "YOUR_ANDROID_API_KEY"
        }
      }
    }
  }
}

2. Install location permissions (if using user location)

npx expo install expo-location

3. Wrap your app with the provider

import { MapProvider } from './map';

function App() {
  return <MapProvider>{/* Your app components */}</MapProvider>;
}

4. Use the Map component or hook

import { Map, useMapContext } from './map';

function MapScreen() {
  const { addMarker } = useMapContext();

  const handleAddMarker = () => {
    addMarker({
      id: '1',
      latitude: 37.78825,
      longitude: -122.4324,
      title: 'San Francisco',
      description: 'City by the Bay',
    });
  };

  return (
    <View style={{ flex: 1 }}>
      <Map
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0.0922,
          longitudeDelta: 0.0421,
        }}
        showsUserLocation
        style={{ flex: 1 }}
      />
      <Button title="Add Marker" onPress={handleAddMarker} />
    </View>
  );
}

API Reference

MapProvider

Wrap your app to provide map context to all child components.

<MapProvider>{children}</MapProvider>

Map Component Props

| Prop | Type | Required | Description | | --------------------- | ---------------------------- | -------- | ----------------------------------------------- | | initialRegion | MapRegion | No | Initial map region (lat, lng, deltas) | | showsUserLocation | boolean | No | Show user's current location (default: false) | | followsUserLocation | boolean | No | Center map on user location (default: false) | | style | ViewStyle | No | Custom styles for the map container | | onMarkerPress | (markerId: string) => void | No | Callback when marker is pressed |

useMapContext Hook

Access map state and methods throughout your app.

State

interface MapState {
  region: MapRegion | null;
  markers: MapMarker[];
  selectedMarkerId: string | null;
}

Methods

Region Management

  • setRegion(region: MapRegion): void - Update map viewport

Marker Management

  • setMarkers(markers: MapMarker[]): void - Replace all markers
  • addMarker(marker: MapMarker): void - Add a single marker
  • removeMarker(markerId: string): void - Remove marker by ID
  • updateMarker(markerId: string, updates: Partial<MapMarker>): void - Update marker properties
  • clearMarkers(): void - Remove all markers

Selection Management

  • selectMarker(markerId: string | null): void - Select/deselect marker

Types

MapRegion

interface MapRegion {
  latitude: number;
  longitude: number;
  latitudeDelta?: number;
  longitudeDelta?: number;
}

MapMarker

interface MapMarker {
  id: string;
  latitude: number;
  longitude: number;
  title?: string;
  description?: string;
  draggable?: boolean;
  icon?: ReactNode;
}

Usage Examples

Basic Map Display

import { Map } from './map';

function SimpleMap() {
  return (
    <Map
      initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.0922,
        longitudeDelta: 0.0421,
      }}
      showsUserLocation
      style={{ flex: 1 }}
    />
  );
}

Adding Markers Dynamically

import { useMapContext } from './map';

function LocationPicker() {
  const { addMarker, mapState } = useMapContext();

  const handleMapPress = (coordinate) => {
    const newMarker = {
      id: Date.now().toString(),
      latitude: coordinate.latitude,
      longitude: coordinate.longitude,
      title: 'New Location',
    };
    addMarker(newMarker);
  };

  return (
    <View>
      <Text>Total Markers: {mapState.markers.length}</Text>
      {/* Map component */}
    </View>
  );
}

Managing Multiple Markers

import { useMapContext } from './map';

function LocationManager() {
  const { setMarkers, clearMarkers, mapState } = useMapContext();

  const loadLocations = async () => {
    const locations = await fetchLocations();
    const markers = locations.map((loc) => ({
      id: loc.id,
      latitude: loc.lat,
      longitude: loc.lng,
      title: loc.name,
      description: loc.address,
    }));
    setMarkers(markers);
  };

  return (
    <View>
      <Button title="Load Locations" onPress={loadLocations} />
      <Button title="Clear All" onPress={clearMarkers} />
      <Text>{mapState.markers.length} locations</Text>
    </View>
  );
}

Marker Selection with Details

import { useMapContext, Map } from './map';

function InteractiveMap() {
  const { mapState, selectMarker } = useMapContext();

  const selectedMarker = mapState.markers.find((m) => m.id === mapState.selectedMarkerId);

  return (
    <View style={{ flex: 1 }}>
      <Map
        showsUserLocation
        style={{ flex: 1 }}
        onMarkerPress={(markerId) => {
          selectMarker(markerId);
        }}
      />
      {selectedMarker && (
        <View style={styles.detailsPanel}>
          <Text style={styles.title}>{selectedMarker.title}</Text>
          <Text>{selectedMarker.description}</Text>
          <Button title="Close" onPress={() => selectMarker(null)} />
        </View>
      )}
    </View>
  );
}

Updating Marker Properties

import { useMapContext } from './map';

function EditableMarkers() {
  const { updateMarker, mapState } = useMapContext();

  const handleEditMarker = (markerId: string) => {
    updateMarker(markerId, {
      title: 'Updated Title',
      description: 'New description',
      draggable: true,
    });
  };

  return (
    <View>
      {mapState.markers.map((marker) => (
        <TouchableOpacity key={marker.id} onPress={() => handleEditMarker(marker.id)}>
          <Text>{marker.title}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
}

Custom Marker Icons

import { useMapContext } from './map';
import { Image } from 'react-native';

function CustomMarkerMap() {
  const { addMarker } = useMapContext();

  const addCustomMarker = () => {
    addMarker({
      id: 'custom-1',
      latitude: 37.78825,
      longitude: -122.4324,
      title: 'Custom Marker',
      icon: <Image source={require('./pin.png')} style={{ width: 30, height: 30 }} />,
    });
  };

  return <Button title="Add Custom Marker" onPress={addCustomMarker} />;
}

Centering Map on Region

import { useMapContext } from './map';

function RegionControls() {
  const { setRegion } = useMapContext();

  const centerOnSF = () => {
    setRegion({
      latitude: 37.78825,
      longitude: -122.4324,
      latitudeDelta: 0.0922,
      longitudeDelta: 0.0421,
    });
  };

  const centerOnNY = () => {
    setRegion({
      latitude: 40.7128,
      longitude: -74.006,
      latitudeDelta: 0.0922,
      longitudeDelta: 0.0421,
    });
  };

  return (
    <View>
      <Button title="San Francisco" onPress={centerOnSF} />
      <Button title="New York" onPress={centerOnNY} />
    </View>
  );
}

Using with Expo Location

import { useMapContext } from './map';
import * as Location from 'expo-location';

function UserLocationMap() {
  const { setRegion, addMarker } = useMapContext();

  useEffect(() => {
    (async () => {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        alert('Permission to access location was denied');
        return;
      }

      const location = await Location.getCurrentPositionAsync({});
      const { latitude, longitude } = location.coords;

      // Center map on user
      setRegion({
        latitude,
        longitude,
        latitudeDelta: 0.01,
        longitudeDelta: 0.01,
      });

      // Add user marker
      addMarker({
        id: 'user',
        latitude,
        longitude,
        title: 'You are here',
      });
    })();
  }, []);

  return <Map showsUserLocation style={{ flex: 1 }} />;
}

Filtering Markers

import { useMapContext } from './map';

function FilteredMap() {
  const { mapState, setMarkers } = useMapContext();
  const [category, setCategory] = useState('all');

  const filterByCategory = (cat: string) => {
    setCategory(cat);
    const filtered = allMarkers.filter((m) => cat === 'all' || m.category === cat);
    setMarkers(filtered);
  };

  return (
    <View>
      <Button title="All" onPress={() => filterByCategory('all')} />
      <Button title="Restaurants" onPress={() => filterByCategory('restaurant')} />
      <Button title="Hotels" onPress={() => filterByCategory('hotel')} />
    </View>
  );
}

Advanced Usage

Route Planning with Multiple Markers

import { useMapContext } from './map';

function RouteMap() {
  const { setMarkers, mapState } = useMapContext();

  const planRoute = (waypoints: Coordinate[]) => {
    const markers = waypoints.map((point, index) => ({
      id: `waypoint-${index}`,
      latitude: point.latitude,
      longitude: point.longitude,
      title: `Stop ${index + 1}`,
      draggable: true,
    }));
    setMarkers(markers);
  };

  return <Map style={{ flex: 1 }} />;
}

Real-time Location Tracking with Expo

import { useMapContext } from './map';
import * as Location from 'expo-location';

function LocationTracker() {
  const { updateMarker, setRegion } = useMapContext();

  useEffect(() => {
    let subscription;

    (async () => {
      const { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') return;

      subscription = await Location.watchPositionAsync(
        {
          accuracy: Location.Accuracy.High,
          timeInterval: 1000,
          distanceInterval: 10,
        },
        (location) => {
          const { latitude, longitude } = location.coords;

          updateMarker('user-location', { latitude, longitude });
          setRegion({
            latitude,
            longitude,
            latitudeDelta: 0.01,
            longitudeDelta: 0.01,
          });
        },
      );
    })();

    return () => subscription?.remove();
  }, []);

  return <Map followsUserLocation style={{ flex: 1 }} />;
}

Common Use Cases

1. Store Locator

Display multiple store locations with search and filtering

2. Delivery Tracking

Show real-time delivery driver location and route

3. Property Listings

Display real estate properties on a map with details

4. Event Finder

Show nearby events with custom markers

5. Route Planning

Create multi-stop routes with draggable waypoints

6. Geo-tagging

Allow users to select locations by tapping the map

Visual Features

Marker Selection Feedback

Selected markers are highlighted (opacity: 1.0) while unselected markers are dimmed (opacity: 0.5), providing clear visual feedback.

Draggable Markers

addMarker({
  id: 'draggable-1',
  latitude: 37.78825,
  longitude: -122.4324,
  draggable: true, // Enable dragging
});

Performance Optimization

  • Memoized callbacks - All context methods use useCallback to prevent unnecessary re-renders
  • Efficient state updates - State updates are batched and optimized
  • Selective rendering - Only affected markers re-render on updates

Expo-Specific Features

Using with Expo Location

npx expo install expo-location
import * as Location from 'expo-location';

const { status } = await Location.requestForegroundPermissionsAsync();
const location = await Location.getCurrentPositionAsync({});

Development Builds

For optimal performance and full feature support, use EAS Build or development builds:

npx expo run:ios
npx expo run:android

Expo Go Limitations

When using Expo Go:

  • Limited map styling options
  • Some native features may not work
  • Consider using development builds for production apps

Best Practices

  1. Always provide unique IDs - Use UUIDs or timestamps for marker IDs
  2. Handle permissions - Request location permissions before enabling user location
  3. Optimize marker count - Consider clustering for 100+ markers
  4. Clean up on unmount - Clear markers when navigating away
  5. Test on real devices - Maps behave differently on simulators
  6. Handle loading states - Show indicators while fetching location data
  7. Provide fallbacks - Handle cases where location services are disabled
  8. Use development builds - Expo Go has limitations; use dev builds for full features

Platform Differences

iOS

  • Requires location permissions in app.json
  • Native Apple Maps integration
  • Better performance with many markers

Android

  • Requires Google Maps API key in app.json
  • Google Maps integration
  • May require Google Play Services

Troubleshooting

Map not showing

  • Check API key configuration in app.json
  • Verify permissions are granted
  • Ensure region has valid coordinates
  • Try using a development build instead of Expo Go

Markers not appearing

  • Verify marker coordinates are within visible region
  • Check that markers array is not empty
  • Ensure marker IDs are unique

Performance issues

  • Reduce number of visible markers
  • Implement marker clustering
  • Optimize custom marker icons
  • Use development builds for better performance

Location permissions not working

import * as Location from 'expo-location';

const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
  Alert.alert('Permission denied', 'Location permission is required');
}

Getting API Keys

Google Maps API Key

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Enable Maps SDK for Android and iOS
  4. Create credentials (API Key)
  5. Add to app.json

TypeScript Support

Fully typed with TypeScript for excellent IDE support and type safety:

const { mapState, addMarker }: MapContextType = useMapContext();