Build Different

Building a Responsive Location Search Component with a React Search Box

Developer Portal
Jul 29, 2020 • Last edit on Jun 21, 20222 min read
Laptop open displaying a split screen: web code and a map

Modern web standards allow developers to deliver location-aware features to users based on device GPS or network information. This information could be useful on its own — but, when combined with a mapping service, this information enables many additional interesting features.

This tutorial will describe how to build a responsive web application with a point of interest (POI) location search feature using the React web framework and the TomTom Search API.

Table of Contents:

Using React & Mobile-first

Why React?

Building the App

TomTom Search API

React-search-box Component

Displaying Place Details

Next Steps

[Using React & Mobile-first]

We’ll use a mobile-first design approach. We can use the Chrome DevTools device toolbar to target different devices.

image1

I usually choose an iPhone 5 because this model uses one of the smallest screens. If our design looks good there, it usually scales up well on larger devices.

image3

A demo of the final product is shown below.

reactgif1

[Why React?]

React is a popular JavaScript library for building user interfaces (UIs). It’s distinguished from other UI frameworks by the following features:

  • Declarative — React allows you to describe what your UI should look like instead of how to construct it. This can make React code easier to understand and debug. This is accomplished using a “reactive” system which automatically updates the UI as data changes.
  • Component-based — The basic building block of a React UI is a component. Traditional UI code separates the presentation (HTML) and business logic (JavaScript). React mixes the presentation and business logic in favor of a separation of concerns.

The TomTom location APIs provide easy-to-use services for mapping, geolocation, routing and navigation, and more. This data can benefit a wide variety of applications. We’ll be using the TomTom Search API, which allows us to search a location for nearby POIs including restaurants, retail stores, bars, and so on.

You can go to https://developer.tomtom.com/ to create an account and obtain an API key. That’s all you will need to follow along.

We’ll use the popular react-search-box component to implement an autocomplete POI search. This will display a collection of changing suggestions as the user types in the search box. The autocomplete suggestions will be populated by the TomTom Search API. This allows the user to query local places and pick from a list of relevant suggestions.

[Building the App]

The goal is to build a React Single Page Application (SPA), so we’ll be using HTML, CSS, and JavaScript as the basic building blocks. The app will also use JSX, React’s syntax extension to JavaScript, to mix HTML templates with JavaScript code.

Building the app consists of the following steps:

  • Scaffold a blank application using Create React App
  • Obtain, store, and display the user’s GPS coordinates using a Banner component
  • Create a PlaceFinder service to query the TomTom Search API
  • Configure the react-search-box to use the TomTom suggestions
  • Create a Place component to display details of the selected place

Start by using Create React App to scaffold the application. This is an officially-supported Command Line Interface (CLI) tool to create new React applications. You’ll need to install the latest version of Node.js if you don’t have it. Then run the following command in your terminal to scaffold the application in a new folder called poi-app. These commands should work in bash or PowerShell, but you may need to modify them for your terminal of choice.

1npx create-react-app poi-app # Create new react app in new poi-app folder
2cd poi-app # Change directory to new app
3npm install react-search-box --save # Add react-search-box

Then run the app using the NPM “start” script.

npm run start

Now that the default React application is up and running, we can start to make it our own.

First build the GPS banner. We can obtain the user’s GPS coordinates using the Geolocation API. This should be done right at the beginning, as our app cannot do anything without the user’s coordinates.

The componentDidMount() component lifecycle method is a good place for this. It is called immediately after the component is inserted into the DOM. Here’s the code:

1componentDidMount() {
2 navigator.geolocation.getCurrentPosition((e) => {
3 this.setState({
4 geoLocation: e.coords
5 });
6 }, async (err) => {
7 this.setState({
8 geoError: err
9 });
10 });
11}

The result and the error are stored in the App component state. Here’s the full component at this point:

1export default class App extends Component {
2
3 constructor(props) {
4 super(props);
5 this.state = {
6 geoLocation: {},
7 geoError: null,
8 searchResults: [],
9 };
10 }
11
12 componentDidMount() {
13 navigator.geolocation.getCurrentPosition((e) => {
14 this.setState({
15 geoLocation: e.coords
16 });
17 }, async (err) => {
18 this.setState({
19 geoError: err
20 });
21 });
22 }
23}

To display this data, we’ll create a Banner component. If the user grants GPS permission, it will display the user’s GPS coordinates. If permission is denied, it will display the error message to the user. Here is the Banner component:

1export default class Banner extends Component {
2 render() {
3 if (this.props.geoError) {
4 return <p className="banner warn">{this.props.geoError.message}</p>;
5 } else if (this.props.geoLocation.latitude) {
6 return <p className="banner success">
7 Lat: <strong>{this.props.geoLocation.latitude.toFixed(4)}</strong>,
8 Long: <strong>{this.props.geoLocation.longitude.toFixed(4)}</strong>
9 </p>;
10 } else {
11 return null
12 }
13 }
14}

Finally, we will render the Banner component in the App’s render() function, passing in the geoLocation and the geoError as props.

1render() {
2 return (
3 <div>
4 <Banner
5 geoLocation={this.state.geoLocation}
6 geoError={this.state.geoError}
7 />
8 </div>
9 );
10}

Here is a reactive diagram of the GPS banner feature. Adding diagrams is an important part of building more serious projects, helping you think through steps at a more component-based level. As we go along, we’ll update this diagram to include the different features we continue to add.

image5

Here is a demo of the banner behavior when granting GPS permissions:

reactgif2

And when denying GPS permissions:

reactgif3

[TomTom Search API]

Next we create a PlaceFinder service to obtain place suggestions using the Points of Interest Search endpoint of the TomTom Search API.

1export default class PlaceFinder {
2 constructor(apiKey) {
3 this.apiKey = apiKey;
4 }
5
6 async getNearbyPlaces(query, lat, long, limit = 5, radius = 10000) {
7 let baseUrl = 'https://api.tomtom.com/search/2/poiSearch';
8 let queryString = `limit=${limit}&lat=${lat}&lon=${long}&radius=${radius}&key=${this.apiKey}`;
9 let response = await axios.get(`${baseUrl}/${query}.json?${queryString}`);
10 return response.data.results;
11 }
12}

The API key is passed into the constructor of the class to be used in all subsequent API calls.

The PlaceFinder service accepts a query which will be the input the user types in. It also accepts the GPS latitude and longitude to narrow POIs down to ones that are nearby. It further accepts optional limit and radius parameters, which are defaulted to 5 and 10000 respectively. The radius is measured in meters.

The Search API returns details about the POI like the name, website, phone number, address, and GPS coordinates.

[The react-search-box Component]

The react-search-box component implements autocomplete functionality for us. All we have to do is feed the query from the search box into the getNearbyPlaces method and populate the suggestions with the results.

Here are the relevant parts of the App component with the react-search-box added:

1async onSearchChange(query) {
2 if (query.length > 0) {
3 let placeFinder = new PlaceFinder('YOUR_API_KEY');
4 let results = (await placeFinder.getNearbyPlaces(query, this.state.geoLocation.latitude, this.state.geoLocation.longitude));
5 this.setState({
6 searchResults: results
7 });
8 }
9}
10
11render() {
12 return (
13 <div>
14 <Banner
15 geoLocation={this.state.geoLocation}
16 geoError={this.state.geoError}
17 />
18
19 <ReactSearchBox
20 placeholder="Search for nearby places"
21 matchedRecords={this.state.searchResults
22 .map(result => ({
23 key: result.id,
24 name: result.poi.name,
25 dist: result.dist,
26 value: `${result.poi.name} | ${(result.dist / 1000).toFixed(2)}km `
27 }))
28 .sort((a, b) => a.dist - b.dist)
29 }
30 data={this.state.searchResults
31 .map(result => ({
32 key: result.id,
33 name: result.poi.name,
34 dist: result.dist,
35 value: result.poi.name
36 }))
37 .sort((a, b) => a.dist - b.dist)
38 }
39 onSelect={(place) => console.log(place)}
40 autoFocus={true}
41 onChange={(query) => this.onSearchChange(query)}
42 fuseConfigs={{
43 minMatchCharLength: 0,
44 threshold: 1,
45 distance: 100000,
46 sort: false
47 }}
48 keys = {['name']}
49 />
50 </div>
51 );
52}

Here is the updated diagram with the react-search-box added:

image6

[Displaying Place Details]

Finally, we can display more details for a POI when the user selects it from the react-search-box. We add a selectedPlace property to the state object and set it in the react-search-box’s onSelect() event.

1setPlace(key) {
2 let place = this.state.searchResults.find((p) => p.id === key);
3 this.setState({
4 selectedPlace: place
5 })
6}
7
8render() {
9 return (
10 <div>
11 ...
12 <ReactSearchBox
13 ...
14 onSelect={(place) => this.setPlace(place.key)}
15 ...
16 />
17 </div>
18 );

Then we need to display the details of the selectedPlace. For this we add a Place component that accepts the POI data from the TomTom API as a prop.

1export default class Place extends Component {
2 render() {
3 if (this.props.data) {
4 return (
5 <div className={this.props.className}>
6 <h1>{this.props.data.poi.name}</h1>
7 <h3>{this.props.data.poi.classifications[0].code} | {(this.props.data.dist / 1000).toFixed(2)}km away</h3>
8 <p>
9 {this.props.data.address.streetNumber
10 + ' '
11 + this.props.data.address.streetName}
12 <br/>
13 {this.props.data.address.municipality
14 + ', ' + this.props.data.address.countrySubdivision
15 + ' ' + this.props.data.address.postalCode}
16 </p>
17 </div>
18 );
19 } else {
20 return null;
21 }
22 }
23}

Then we add the Place component to the end of the App component’s render function.

1<Place
2 className="place-box"
3 data={this.state.selectedPlace}>
4</Place>

Here is the final diagram showing the application with all components.

image8

After a little CSS magic (the stylesheet can be found in the linked repository), here’s what we have.

reactgif1

[Where to Next?]

Let’s recap our accomplishments:

  • We took advantage of the GeoLocation API to determine the user’s GPS location.
  • We obtained nearby places using TomTom’s Search API.
  • We used React to build a simple application leveraging a popular autocomplete search box component and our own custom components.

From here we could continue adding functionality depending on our goals:

  • Use additional APIs to obtain traffic or routing information from the user’s location.
  • Use mappings services to render the select POI on a map.
  • Integrate with online menus or delivery services to display additional information.

Additionally, we called the Search API directly here. The TomTom Maps SDK for Web lets you easily add maps to your app and has built-in helper functions for accessing TomTom location API services.

Using location data and thinking spatially allows developers to engage the user in new and exciting ways.

You can check out one of our other React tutorials here: Adding TomTom Maps to a Modern React App.

Visit the TomTom Developer Portal to register for your free API key and get started on your next project.

The possibilities are endless and you’re off to a good start!

Happy mapping!

VisitOurDeveloperForum

Get the developer
newsletter.

No marketing fuff. Tech content only.
Thanks for contacting us

We will reach out to you soon.
Blog cards
tomtom tech news