React Google Maps Library - Extended Component Library

This example shows how to build a basic locations services web app using the Google Maps Platform's Extended Component Library with the vis.gl/react-google-maps open source library.

Google Maps Platform's Extended Component Library is a set of Web Components that helps developers build better maps faster, and with less effort. It encapsulates boilerplate code, best practices, and responsive design, reducing complex map UIs into what is effectively a single HTML element. These components make it easier to read, learn, customize, and maintain maps-related code.

The vis.gl/react-google-maps library is a collection of React components and hooks for the Google Maps JavaScript API.

TypeScript

import React, { useState, useRef } from 'react';
import ReactDOM from 'react-dom/client';
import { AdvancedMarker, Map, Pin, APIProvider } from '@vis.gl/react-google-maps';
import {
  PlaceReviews,
  PlaceDataProvider,
  PlaceDirectionsButton,
  IconButton,
  PlaceOverview,
  SplitLayout,
  OverlayLayout,
  PlacePicker
} from '@googlemaps/extended-component-library/react';
/**
 * The below imports are necessary because we are creating refs of 
 * the OverlayLayout and PlacePicker components. You need to pass 
 * the ref property a web component type object. Imports from  
 * @googlemaps/extended-component-library/react are wrappers around
 * the web components, not the components themselves. For the ref 
 * property we import the actual components and alias them for clarity.
 */
import { OverlayLayout as TOverlayLayout } from '@googlemaps/extended-component-library/overlay_layout.js';
import { PlacePicker as TPlacePicker } from '@googlemaps/extended-component-library/place_picker.js';

const API_KEY =
  globalThis.GOOGLE_MAPS_API_KEY ?? ("YOUR_API_KEY");
const DEFAULT_CENTER = { lat: 38, lng: -98 };
const DEFAULT_ZOOM = 4;
const DEFAULT_ZOOM_WITH_LOCATION = 16;
/**
 * Sample app that helps users locate a college on the map, with place info such
 * as ratings, photos, and reviews displayed on the side.
 */
const App = () => {
  const overlayLayoutRef = useRef<TOverlayLayout>(null);
  const pickerRef = useRef<TPlacePicker>(null);
  const [college, setCollege] = useState<google.maps.places.Place | undefined>(undefined);
  /**
   * See https://lit.dev/docs/frameworks/react/#using-slots for why
   * we need to wrap our custom elements in a div with a slot attribute. 
   */
  return (
    <div className="App">
      <APIProvider
        solutionChannel='GMP_devsite_samples_v3_rgmcollegepicker'
        apiKey={API_KEY}
        version='beta' >
        <SplitLayout rowReverse rowLayoutMinWidth={700}>
          <div className="SlotDiv" slot="fixed">
            <OverlayLayout ref={overlayLayoutRef}>
              <div className="SlotDiv" slot="main">
                <PlacePicker
                  className="CollegePicker"
                  ref={pickerRef}
                  forMap="gmap"
                  country={['us', 'ca']}
                  type="university"
                  placeholder="Enter a college in the US or Canada"
                  onPlaceChange={() => {
                    if (!pickerRef.current?.value) {
                      setCollege(undefined);
                    } else {
                      setCollege(pickerRef.current?.value);
                    }
                  }}
                />
                <PlaceOverview
                  size="large"
                  place={college}
                  googleLogoAlreadyDisplayed
                >
                  <div slot="action" className="SlotDiv">
                    <IconButton
                      slot="action"
                      variant="filled"
                      onClick={() => overlayLayoutRef.current?.showOverlay()}
                    >
                      See Reviews
                    </IconButton>
                  </div>
                  <div slot="action" className="SlotDiv">
                    <PlaceDirectionsButton slot="action" variant="filled">
                      Directions
                    </PlaceDirectionsButton>
                  </div>
                </PlaceOverview>
              </div>
              <div slot="overlay" className="SlotDiv">
                <IconButton
                  className="CloseButton"
                  onClick={() => overlayLayoutRef.current?.hideOverlay()}
                >
                  Close
                </IconButton>
                <PlaceDataProvider place={college}>
                  <PlaceReviews />
                </PlaceDataProvider>
              </div>
            </OverlayLayout>
          </div>
          <div className="SplitLayoutContainer" slot="main">
            <Map
              id="gmap"
              mapId="8c732c82e4ec29d9"
              center={college?.location ?? DEFAULT_CENTER}
              zoom={college?.location ? DEFAULT_ZOOM_WITH_LOCATION : DEFAULT_ZOOM}
              gestureHandling="none"
              fullscreenControl={false}
              zoomControl={false}
            >
              {college?.location && (
                <AdvancedMarker position={college?.location}>
                  <Pin background={'#FBBC04'} glyphColor={'#000'} borderColor={'#000'} />
                </AdvancedMarker>
              )}
            </Map>
          </div>
        </SplitLayout>
      </APIProvider>
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

JavaScript

import React, { useState, useRef } from "react";
import ReactDOM from "react-dom/client";
import {
  AdvancedMarker,
  Map,
  Pin,
  APIProvider,
} from "@vis.gl/react-google-maps";
import {
  PlaceReviews,
  PlaceDataProvider,
  PlaceDirectionsButton,
  IconButton,
  PlaceOverview,
  SplitLayout,
  OverlayLayout,
  PlacePicker,
} from "@googlemaps/extended-component-library/react";
const API_KEY = globalThis.GOOGLE_MAPS_API_KEY ?? "YOUR_API_KEY";
const DEFAULT_CENTER = { lat: 38, lng: -98 };
const DEFAULT_ZOOM = 4;
const DEFAULT_ZOOM_WITH_LOCATION = 16;

/**
 * Sample app that helps users locate a college on the map, with place info such
 * as ratings, photos, and reviews displayed on the side.
 */
const App = () => {
  const overlayLayoutRef = useRef(null);
  const pickerRef = useRef(null);
  const [college, setCollege] = useState(undefined);
  /**
   * See https://lit.dev/docs/frameworks/react/#using-slots for why
   * we need to wrap our custom elements in a div with a slot attribute.
   */
  return (
    <div className="App">
      <APIProvider
        solutionChannel="GMP_devsite_samples_v3_rgmcollegepicker"
        apiKey={API_KEY}
        version="beta"
      >
        <SplitLayout rowReverse rowLayoutMinWidth={700}>
          <div className="SlotDiv" slot="fixed">
            <OverlayLayout ref={overlayLayoutRef}>
              <div className="SlotDiv" slot="main">
                <PlacePicker
                  className="CollegePicker"
                  ref={pickerRef}
                  forMap="gmap"
                  country={["us", "ca"]}
                  type="university"
                  placeholder="Enter a college in the US or Canada"
                  onPlaceChange={() => {
                    if (!pickerRef.current?.value) {
                      setCollege(undefined);
                    } else {
                      setCollege(pickerRef.current?.value);
                    }
                  }}
                />
                <PlaceOverview
                  size="large"
                  place={college}
                  googleLogoAlreadyDisplayed
                >
                  <div slot="action" className="SlotDiv">
                    <IconButton
                      slot="action"
                      variant="filled"
                      onClick={() => overlayLayoutRef.current?.showOverlay()}
                    >
                      See Reviews
                    </IconButton>
                  </div>
                  <div slot="action" className="SlotDiv">
                    <PlaceDirectionsButton slot="action" variant="filled">
                      Directions
                    </PlaceDirectionsButton>
                  </div>
                </PlaceOverview>
              </div>
              <div slot="overlay" className="SlotDiv">
                <IconButton
                  className="CloseButton"
                  onClick={() => overlayLayoutRef.current?.hideOverlay()}
                >
                  Close
                </IconButton>
                <PlaceDataProvider place={college}>
                  <PlaceReviews />
                </PlaceDataProvider>
              </div>
            </OverlayLayout>
          </div>
          <div className="SplitLayoutContainer" slot="main">
            <Map
              id="gmap"
              mapId="8c732c82e4ec29d9"
              center={college?.location ?? DEFAULT_CENTER}
              zoom={
                college?.location ? DEFAULT_ZOOM_WITH_LOCATION : DEFAULT_ZOOM
              }
              gestureHandling="none"
              fullscreenControl={false}
              zoomControl={false}
            >
              {college?.location && (
                <AdvancedMarker position={college?.location}>
                  <Pin
                    background={"#FBBC04"}
                    glyphColor={"#000"}
                    borderColor={"#000"}
                  />
                </AdvancedMarker>
              )}
            </Map>
          </div>
        </SplitLayout>
      </APIProvider>
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

CSS

body {
  margin: 0;
  font-family: sans-serif;
}

#root {
  width: 100vw;
  height: 100vh;
}

.App {
  --gmpx-color-surface: #f6f5ff;
  --gmpx-color-on-primary: #f8e8ff;
  --gmpx-color-on-surface: #000;
  --gmpx-color-on-surface-variant: #636268;
  --gmpx-color-primary: #8a5cf4;
  --gmpx-fixed-panel-height-column-layout: 420px;
  --gmpx-fixed-panel-width-row-layout: 340px;
  background: var(--gmpx-color-surface);
  inset: 0;
  position: fixed;
}

.MainContainer {
  display: flex;
  flex-direction: column;
}

.SplitLayoutContainer {
  height: 100%;
}

.CollegePicker {
  --gmpx-color-surface: #fff;
  flex-grow: 1;
  margin: 1rem;
}

.CloseButton {
  display: block;
  margin: 1rem;
}

.SlotDiv {
  display: contents;
}

HTML

<html>
  <head>
    <title>React Google Maps - College Picker App</title>

    <link rel="stylesheet" type="text/css" href="./style.css" />
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="./index"></script>
  </body>
</html>

Try Sample

Clone Sample

Git and Node.js are required to run this sample locally. Follow these instructions to install Node.js and NPM. The following commands clone, install dependencies and start the sample application.

  git clone -b sample-rgm-college-picker https://github.com/googlemaps/js-samples.git
  cd js-samples
  npm i
  npm start

Other samples can be tried by switching to any branch beginning with sample-SAMPLE_NAME.

  git checkout sample-SAMPLE_NAME
  npm i
  npm start