0
# Places & Search
1
2
Google Places API integration components for location search, autocomplete functionality, and place details. These components provide search capabilities within map contexts.
3
4
## Capabilities
5
6
### SearchBox
7
8
Map-bound search box component that provides Google Places autocomplete functionality within a map context.
9
10
```javascript { .api }
11
/**
12
* Map-bound search box component with Google Places integration
13
*/
14
class SearchBox extends Component<SearchBoxProps> {
15
/** Returns the current search bounds */
16
getBounds(): google.maps.LatLngBounds;
17
/** Returns the array of selected places */
18
getPlaces(): google.maps.places.PlaceResult[];
19
}
20
21
interface SearchBoxProps {
22
/** Position of the control on the map */
23
controlPosition?: number;
24
/** Default bounds for biasing search results */
25
defaultBounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
26
/** Controlled bounds for biasing search results */
27
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
28
/** Callback when places selection changes */
29
onPlacesChanged?(): void;
30
}
31
```
32
33
**Usage Example:**
34
35
```javascript
36
import SearchBox from "react-google-maps/lib/components/places/SearchBox";
37
38
const MapWithSearch = () => {
39
const [searchBox, setSearchBox] = useState(null);
40
const [markers, setMarkers] = useState([]);
41
42
const onPlacesChanged = () => {
43
const places = searchBox.getPlaces();
44
45
// Clear existing markers
46
setMarkers([]);
47
48
if (places.length === 0) {
49
return;
50
}
51
52
// Create markers for each place
53
const newMarkers = places.map(place => ({
54
position: {
55
lat: place.geometry.location.lat(),
56
lng: place.geometry.location.lng()
57
},
58
title: place.name,
59
place: place
60
}));
61
62
setMarkers(newMarkers);
63
64
// Fit map bounds to show all places
65
const bounds = new google.maps.LatLngBounds();
66
places.forEach(place => {
67
if (place.geometry.viewport) {
68
bounds.union(place.geometry.viewport);
69
} else {
70
bounds.extend(place.geometry.location);
71
}
72
});
73
// map.fitBounds(bounds);
74
};
75
76
return (
77
<GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
78
<SearchBox
79
ref={ref => setSearchBox(ref)}
80
bounds={new google.maps.LatLngBounds(
81
new google.maps.LatLng(37.7049, -122.4794),
82
new google.maps.LatLng(37.8449, -122.3594)
83
)}
84
controlPosition={google.maps.ControlPosition.TOP_LEFT}
85
onPlacesChanged={onPlacesChanged}
86
>
87
<input
88
type="text"
89
placeholder="Search for places..."
90
style={{
91
boxSizing: 'border-box',
92
border: '1px solid transparent',
93
width: '240px',
94
height: '32px',
95
marginTop: '27px',
96
padding: '0 12px',
97
borderRadius: '3px',
98
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.3)',
99
fontSize: '14px',
100
outline: 'none',
101
textOverflow: 'ellipsis',
102
}}
103
/>
104
</SearchBox>
105
106
{markers.map((marker, index) => (
107
<Marker
108
key={index}
109
position={marker.position}
110
title={marker.title}
111
/>
112
))}
113
</GoogleMap>
114
);
115
};
116
```
117
118
### StandaloneSearchBox
119
120
Standalone search box component that works independently of a map context, useful for search forms and location pickers.
121
122
```javascript { .api }
123
/**
124
* Standalone search box component independent of map context
125
*/
126
class StandaloneSearchBox extends Component<StandaloneSearchBoxProps> {
127
/** Returns the current search bounds */
128
getBounds(): google.maps.LatLngBounds;
129
/** Returns the array of selected places */
130
getPlaces(): google.maps.places.PlaceResult[];
131
}
132
133
interface StandaloneSearchBoxProps {
134
/** Default bounds for biasing search results */
135
defaultBounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
136
/** Controlled bounds for biasing search results */
137
bounds?: google.maps.LatLngBounds | google.maps.LatLngBoundsLiteral;
138
/** Callback when places selection changes */
139
onPlacesChanged?(): void;
140
}
141
```
142
143
**Usage Example:**
144
145
```javascript
146
import StandaloneSearchBox from "react-google-maps/lib/components/places/StandaloneSearchBox";
147
148
const LocationPicker = ({ onLocationSelect }) => {
149
const [searchBox, setSearchBox] = useState(null);
150
const [selectedPlace, setSelectedPlace] = useState(null);
151
152
const onPlacesChanged = () => {
153
const places = searchBox.getPlaces();
154
155
if (places.length > 0) {
156
const place = places[0];
157
const location = {
158
lat: place.geometry.location.lat(),
159
lng: place.geometry.location.lng(),
160
name: place.name,
161
address: place.formatted_address,
162
placeId: place.place_id,
163
types: place.types
164
};
165
166
setSelectedPlace(location);
167
if (onLocationSelect) {
168
onLocationSelect(location);
169
}
170
}
171
};
172
173
return (
174
<div style={{ padding: '20px', maxWidth: '400px' }}>
175
<h3>Select a Location</h3>
176
177
<StandaloneSearchBox
178
ref={ref => setSearchBox(ref)}
179
bounds={new google.maps.LatLngBounds(
180
new google.maps.LatLng(37.7049, -122.4794),
181
new google.maps.LatLng(37.8449, -122.3594)
182
)}
183
onPlacesChanged={onPlacesChanged}
184
>
185
<input
186
type="text"
187
placeholder="Search for a location..."
188
style={{
189
width: '100%',
190
padding: '12px',
191
fontSize: '16px',
192
border: '1px solid #ddd',
193
borderRadius: '4px',
194
outline: 'none'
195
}}
196
/>
197
</StandaloneSearchBox>
198
199
{selectedPlace && (
200
<div style={{
201
marginTop: '16px',
202
padding: '12px',
203
backgroundColor: '#f5f5f5',
204
borderRadius: '4px'
205
}}>
206
<h4>{selectedPlace.name}</h4>
207
<p>{selectedPlace.address}</p>
208
<p>
209
<strong>Coordinates:</strong> {selectedPlace.lat.toFixed(6)}, {selectedPlace.lng.toFixed(6)}
210
</p>
211
<p>
212
<strong>Types:</strong> {selectedPlace.types.join(', ')}
213
</p>
214
</div>
215
)}
216
</div>
217
);
218
};
219
```
220
221
## Google Places API Setup
222
223
### API Requirements
224
225
Places components require the Google Places API to be enabled and the places library to be loaded:
226
227
```javascript
228
const googleMapURL = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places`;
229
230
const MapComponent = withScriptjs(withGoogleMap(() => (
231
<GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
232
<SearchBox>
233
<input type="text" placeholder="Search..." />
234
</SearchBox>
235
</GoogleMap>
236
)));
237
238
<MapComponent
239
googleMapURL={googleMapURL}
240
loadingElement={<div style={{ height: "100%" }} />}
241
containerElement={<div style={{ height: "400px" }} />}
242
mapElement={<div style={{ height: "100%" }} />}
243
/>
244
```
245
246
## Advanced Places Patterns
247
248
### Search with Custom Filters
249
250
Filter search results by place types:
251
252
```javascript
253
const RestaurantSearchBox = () => {
254
const [searchBox, setSearchBox] = useState(null);
255
const [places, setPlaces] = useState([]);
256
257
const onPlacesChanged = () => {
258
const allPlaces = searchBox.getPlaces();
259
260
// Filter for restaurants only
261
const restaurants = allPlaces.filter(place =>
262
place.types.includes('restaurant') ||
263
place.types.includes('food') ||
264
place.types.includes('meal_takeaway')
265
);
266
267
setPlaces(restaurants);
268
};
269
270
return (
271
<div>
272
<StandaloneSearchBox
273
ref={ref => setSearchBox(ref)}
274
onPlacesChanged={onPlacesChanged}
275
>
276
<input
277
type="text"
278
placeholder="Search for restaurants..."
279
style={{ width: '300px', padding: '8px' }}
280
/>
281
</StandaloneSearchBox>
282
283
<div style={{ marginTop: '16px' }}>
284
{places.map((place, index) => (
285
<div key={index} style={{ marginBottom: '12px', padding: '8px', border: '1px solid #ddd' }}>
286
<h4>{place.name}</h4>
287
<p>{place.formatted_address}</p>
288
<p>Rating: {place.rating || 'No rating'}</p>
289
<p>Types: {place.types.join(', ')}</p>
290
</div>
291
))}
292
</div>
293
</div>
294
);
295
};
296
```
297
298
### Search with Geolocation Bias
299
300
Bias search results based on user's current location:
301
302
```javascript
303
const LocationBiasedSearch = () => {
304
const [searchBox, setSearchBox] = useState(null);
305
const [userLocation, setUserLocation] = useState(null);
306
const [searchBounds, setSearchBounds] = useState(null);
307
308
useEffect(() => {
309
// Get user's current location
310
if (navigator.geolocation) {
311
navigator.geolocation.getCurrentPosition(
312
(position) => {
313
const userPos = {
314
lat: position.coords.latitude,
315
lng: position.coords.longitude
316
};
317
setUserLocation(userPos);
318
319
// Create search bounds around user location (5km radius)
320
const bounds = new google.maps.LatLngBounds();
321
const center = new google.maps.LatLng(userPos.lat, userPos.lng);
322
const radius = 0.045; // Approximately 5km
323
324
bounds.extend(new google.maps.LatLng(
325
userPos.lat + radius,
326
userPos.lng + radius
327
));
328
bounds.extend(new google.maps.LatLng(
329
userPos.lat - radius,
330
userPos.lng - radius
331
));
332
333
setSearchBounds(bounds);
334
},
335
(error) => {
336
console.error("Geolocation error:", error);
337
}
338
);
339
}
340
}, []);
341
342
return (
343
<StandaloneSearchBox
344
ref={ref => setSearchBox(ref)}
345
bounds={searchBounds}
346
onPlacesChanged={() => {
347
const places = searchBox.getPlaces();
348
console.log("Places near you:", places);
349
}}
350
>
351
<input
352
type="text"
353
placeholder={userLocation ? "Search near your location..." : "Search..."}
354
style={{ width: '300px', padding: '8px' }}
355
/>
356
</StandaloneSearchBox>
357
);
358
};
359
```
360
361
### Autocomplete with Place Details
362
363
Get detailed place information using the Places API:
364
365
```javascript
366
const DetailedPlaceSearch = () => {
367
const [searchBox, setSearchBox] = useState(null);
368
const [placeDetails, setPlaceDetails] = useState(null);
369
370
const onPlacesChanged = () => {
371
const places = searchBox.getPlaces();
372
373
if (places.length > 0) {
374
const place = places[0];
375
376
// Get additional place details
377
const service = new google.maps.places.PlacesService(
378
document.createElement('div')
379
);
380
381
service.getDetails({
382
placeId: place.place_id,
383
fields: [
384
'name', 'formatted_address', 'geometry', 'rating',
385
'photos', 'opening_hours', 'formatted_phone_number',
386
'website', 'reviews', 'price_level'
387
]
388
}, (detailedPlace, status) => {
389
if (status === google.maps.places.PlacesServiceStatus.OK) {
390
setPlaceDetails(detailedPlace);
391
}
392
});
393
}
394
};
395
396
return (
397
<div>
398
<StandaloneSearchBox
399
ref={ref => setSearchBox(ref)}
400
onPlacesChanged={onPlacesChanged}
401
>
402
<input
403
type="text"
404
placeholder="Search for detailed place info..."
405
style={{ width: '400px', padding: '12px' }}
406
/>
407
</StandaloneSearchBox>
408
409
{placeDetails && (
410
<div style={{ marginTop: '20px', padding: '16px', border: '1px solid #ddd' }}>
411
<h2>{placeDetails.name}</h2>
412
<p><strong>Address:</strong> {placeDetails.formatted_address}</p>
413
<p><strong>Rating:</strong> {placeDetails.rating}/5</p>
414
<p><strong>Phone:</strong> {placeDetails.formatted_phone_number}</p>
415
{placeDetails.website && (
416
<p><strong>Website:</strong> <a href={placeDetails.website} target="_blank" rel="noopener noreferrer">{placeDetails.website}</a></p>
417
)}
418
{placeDetails.opening_hours && (
419
<div>
420
<strong>Hours:</strong>
421
<ul>
422
{placeDetails.opening_hours.weekday_text.map((day, index) => (
423
<li key={index}>{day}</li>
424
))}
425
</ul>
426
</div>
427
)}
428
</div>
429
)}
430
</div>
431
);
432
};
433
```