CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-google-maps

React.js Google Maps integration component library providing comprehensive Google Maps functionality through React components

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

places.mddocs/

Places & Search

Google Places API integration components for location search, autocomplete functionality, and place details. These components provide search capabilities within map contexts.

Capabilities

SearchBox

Map-bound search box component that provides Google Places autocomplete functionality within a map context.

/**
 * Map-bound search box component with Google Places integration
 */
class SearchBox extends Component<SearchBoxProps> {
  /** Returns the current search bounds */
  getBounds(): google.maps.LatLngBounds;
  /** Returns the array of selected places */
  getPlaces(): google.maps.places.PlaceResult[];
}

interface SearchBoxProps {
  /** Position of the control on the map */
  controlPosition?: number;
  /** Default bounds for biasing search results */
  defaultBounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
  /** Controlled bounds for biasing search results */
  bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
  /** Callback when places selection changes */
  onPlacesChanged?(): void;
}

Usage Example:

import SearchBox from "react-google-maps/lib/components/places/SearchBox";

const MapWithSearch = () => {
  const [searchBox, setSearchBox] = useState(null);
  const [markers, setMarkers] = useState([]);

  const onPlacesChanged = () => {
    const places = searchBox.getPlaces();
    
    // Clear existing markers
    setMarkers([]);
    
    if (places.length === 0) {
      return;
    }

    // Create markers for each place
    const newMarkers = places.map(place => ({
      position: {
        lat: place.geometry.location.lat(),
        lng: place.geometry.location.lng()
      },
      title: place.name,
      place: place
    }));
    
    setMarkers(newMarkers);

    // Fit map bounds to show all places
    const bounds = new google.maps.LatLngBounds();
    places.forEach(place => {
      if (place.geometry.viewport) {
        bounds.union(place.geometry.viewport);
      } else {
        bounds.extend(place.geometry.location);
      }
    });
    // map.fitBounds(bounds);
  };

  return (
    <GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
      <SearchBox
        ref={ref => setSearchBox(ref)}
        bounds={new google.maps.LatLngBounds(
          new google.maps.LatLng(37.7049, -122.4794),
          new google.maps.LatLng(37.8449, -122.3594)
        )}
        controlPosition={google.maps.ControlPosition.TOP_LEFT}
        onPlacesChanged={onPlacesChanged}
      >
        <input
          type="text"
          placeholder="Search for places..."
          style={{
            boxSizing: 'border-box',
            border: '1px solid transparent',
            width: '240px',
            height: '32px',
            marginTop: '27px',
            padding: '0 12px',
            borderRadius: '3px',
            boxShadow: '0 2px 6px rgba(0, 0, 0, 0.3)',
            fontSize: '14px',
            outline: 'none',
            textOverflow: 'ellipsis',
          }}
        />
      </SearchBox>
      
      {markers.map((marker, index) => (
        <Marker
          key={index}
          position={marker.position}
          title={marker.title}
        />
      ))}
    </GoogleMap>
  );
};

StandaloneSearchBox

Standalone search box component that works independently of a map context, useful for search forms and location pickers.

/**
 * Standalone search box component independent of map context
 */
class StandaloneSearchBox extends Component<StandaloneSearchBoxProps> {
  /** Returns the current search bounds */
  getBounds(): google.maps.LatLngBounds;
  /** Returns the array of selected places */
  getPlaces(): google.maps.places.PlaceResult[];
}

interface StandaloneSearchBoxProps {
  /** Default bounds for biasing search results */
  defaultBounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
  /** Controlled bounds for biasing search results */
  bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
  /** Callback when places selection changes */
  onPlacesChanged?(): void;
}

Usage Example:

import StandaloneSearchBox from "react-google-maps/lib/components/places/StandaloneSearchBox";

const LocationPicker = ({ onLocationSelect }) => {
  const [searchBox, setSearchBox] = useState(null);
  const [selectedPlace, setSelectedPlace] = useState(null);

  const onPlacesChanged = () => {
    const places = searchBox.getPlaces();
    
    if (places.length > 0) {
      const place = places[0];
      const location = {
        lat: place.geometry.location.lat(),
        lng: place.geometry.location.lng(),
        name: place.name,
        address: place.formatted_address,
        placeId: place.place_id,
        types: place.types
      };
      
      setSelectedPlace(location);
      if (onLocationSelect) {
        onLocationSelect(location);
      }
    }
  };

  return (
    <div style={{ padding: '20px', maxWidth: '400px' }}>
      <h3>Select a Location</h3>
      
      <StandaloneSearchBox
        ref={ref => setSearchBox(ref)}
        bounds={new google.maps.LatLngBounds(
          new google.maps.LatLng(37.7049, -122.4794),
          new google.maps.LatLng(37.8449, -122.3594)
        )}
        onPlacesChanged={onPlacesChanged}
      >
        <input
          type="text"
          placeholder="Search for a location..."
          style={{
            width: '100%',
            padding: '12px',
            fontSize: '16px',
            border: '1px solid #ddd',
            borderRadius: '4px',
            outline: 'none'
          }}
        />
      </StandaloneSearchBox>
      
      {selectedPlace && (
        <div style={{ 
          marginTop: '16px', 
          padding: '12px', 
          backgroundColor: '#f5f5f5', 
          borderRadius: '4px' 
        }}>
          <h4>{selectedPlace.name}</h4>
          <p>{selectedPlace.address}</p>
          <p>
            <strong>Coordinates:</strong> {selectedPlace.lat.toFixed(6)}, {selectedPlace.lng.toFixed(6)}
          </p>
          <p>
            <strong>Types:</strong> {selectedPlace.types.join(', ')}
          </p>
        </div>
      )}
    </div>
  );
};

Google Places API Setup

API Requirements

Places components require the Google Places API to be enabled and the places library to be loaded:

const googleMapURL = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places`;

const MapComponent = withScriptjs(withGoogleMap(() => (
  <GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
    <SearchBox>
      <input type="text" placeholder="Search..." />
    </SearchBox>
  </GoogleMap>
)));

<MapComponent
  googleMapURL={googleMapURL}
  loadingElement={<div style={{ height: "100%" }} />}
  containerElement={<div style={{ height: "400px" }} />}
  mapElement={<div style={{ height: "100%" }} />}
/>

Advanced Places Patterns

Search with Custom Filters

Filter search results by place types:

const RestaurantSearchBox = () => {
  const [searchBox, setSearchBox] = useState(null);
  const [places, setPlaces] = useState([]);

  const onPlacesChanged = () => {
    const allPlaces = searchBox.getPlaces();
    
    // Filter for restaurants only
    const restaurants = allPlaces.filter(place => 
      place.types.includes('restaurant') || 
      place.types.includes('food') ||
      place.types.includes('meal_takeaway')
    );
    
    setPlaces(restaurants);
  };

  return (
    <div>
      <StandaloneSearchBox
        ref={ref => setSearchBox(ref)}
        onPlacesChanged={onPlacesChanged}
      >
        <input
          type="text"
          placeholder="Search for restaurants..."
          style={{ width: '300px', padding: '8px' }}
        />
      </StandaloneSearchBox>
      
      <div style={{ marginTop: '16px' }}>
        {places.map((place, index) => (
          <div key={index} style={{ marginBottom: '12px', padding: '8px', border: '1px solid #ddd' }}>
            <h4>{place.name}</h4>
            <p>{place.formatted_address}</p>
            <p>Rating: {place.rating || 'No rating'}</p>
            <p>Types: {place.types.join(', ')}</p>
          </div>
        ))}
      </div>
    </div>
  );
};

Search with Geolocation Bias

Bias search results based on user's current location:

const LocationBiasedSearch = () => {
  const [searchBox, setSearchBox] = useState(null);
  const [userLocation, setUserLocation] = useState(null);
  const [searchBounds, setSearchBounds] = useState(null);

  useEffect(() => {
    // Get user's current location
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const userPos = {
            lat: position.coords.latitude,
            lng: position.coords.longitude
          };
          setUserLocation(userPos);
          
          // Create search bounds around user location (5km radius)
          const bounds = new google.maps.LatLngBounds();
          const center = new google.maps.LatLng(userPos.lat, userPos.lng);
          const radius = 0.045; // Approximately 5km
          
          bounds.extend(new google.maps.LatLng(
            userPos.lat + radius, 
            userPos.lng + radius
          ));
          bounds.extend(new google.maps.LatLng(
            userPos.lat - radius, 
            userPos.lng - radius
          ));
          
          setSearchBounds(bounds);
        },
        (error) => {
          console.error("Geolocation error:", error);
        }
      );
    }
  }, []);

  return (
    <StandaloneSearchBox
      ref={ref => setSearchBox(ref)}
      bounds={searchBounds}
      onPlacesChanged={() => {
        const places = searchBox.getPlaces();
        console.log("Places near you:", places);
      }}
    >
      <input
        type="text"
        placeholder={userLocation ? "Search near your location..." : "Search..."}
        style={{ width: '300px', padding: '8px' }}
      />
    </StandaloneSearchBox>
  );
};

Autocomplete with Place Details

Get detailed place information using the Places API:

const DetailedPlaceSearch = () => {
  const [searchBox, setSearchBox] = useState(null);
  const [placeDetails, setPlaceDetails] = useState(null);

  const onPlacesChanged = () => {
    const places = searchBox.getPlaces();
    
    if (places.length > 0) {
      const place = places[0];
      
      // Get additional place details
      const service = new google.maps.places.PlacesService(
        document.createElement('div')
      );
      
      service.getDetails({
        placeId: place.place_id,
        fields: [
          'name', 'formatted_address', 'geometry', 'rating', 
          'photos', 'opening_hours', 'formatted_phone_number',
          'website', 'reviews', 'price_level'
        ]
      }, (detailedPlace, status) => {
        if (status === google.maps.places.PlacesServiceStatus.OK) {
          setPlaceDetails(detailedPlace);
        }
      });
    }
  };

  return (
    <div>
      <StandaloneSearchBox
        ref={ref => setSearchBox(ref)}
        onPlacesChanged={onPlacesChanged}
      >
        <input
          type="text"
          placeholder="Search for detailed place info..."
          style={{ width: '400px', padding: '12px' }}
        />
      </StandaloneSearchBox>
      
      {placeDetails && (
        <div style={{ marginTop: '20px', padding: '16px', border: '1px solid #ddd' }}>
          <h2>{placeDetails.name}</h2>
          <p><strong>Address:</strong> {placeDetails.formatted_address}</p>
          <p><strong>Rating:</strong> {placeDetails.rating}/5</p>
          <p><strong>Phone:</strong> {placeDetails.formatted_phone_number}</p>
          {placeDetails.website && (
            <p><strong>Website:</strong> <a href={placeDetails.website} target="_blank" rel="noopener noreferrer">{placeDetails.website}</a></p>
          )}
          {placeDetails.opening_hours && (
            <div>
              <strong>Hours:</strong>
              <ul>
                {placeDetails.opening_hours.weekday_text.map((day, index) => (
                  <li key={index}>{day}</li>
                ))}
              </ul>
            </div>
          )}
        </div>
      )}
    </div>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-react-google-maps

docs

addons.md

advanced.md

core-components.md

drawing.md

hocs.md

index.md

layers.md

places.md

shapes.md

visualization.md

tile.json