Adding a Search Bar
This example demonstrates how to add a search bar to your application using the Search component. Search
provides autocomplete location suggestions through the Places API (opens in a new tab), keyboard navigation, customizable styling and more.
Key Features
- Autocomplete location search
- Keyboard navigation support
- Accessible combobox pattern
- Customizable styling
- Loading and clear states
Implementation
- Create a formatter function for search results:
const formatResult = (result: WeatherPlaceSearchResult) => {
if ('place' in result) {
const { place } = result;
const parts = [
place.name,
place.state ? place.state.toUpperCase() : null,
place.country === 'US' ? null : place.countryFull
].filter(Boolean);
return parts.join(', ');
}
return '';
};
- Create the search bar component with an input field, loading spinner, and clear button:
import { Search } from '@xweather/maps-ui-sdk';
const SearchBar = () => (
<Search.Bar className="bg-white rounded-lg shadow-lg px-2 py-1 gap-2">
<Search.SearchIcon className="text-slate-500" />
<Search.Input
placeholder="Search locations"
className="px-1 py-0.5 bg-white selection:bg-[#AAF0E4]"
/>
<Search.LoadingSpinner />
<Search.Clear />
</Search.Bar>
);
- Create the search results component:
import { Search } from '@xweather/maps-ui-sdk';
const SearchResults = () => (
<Search.ResultsFetcher>
<Search.ResultsData>
{({ hasResults }) => (
hasResults ? (
<Search.List>
<Search.ScrollableArea className="py-4 mt-2 bg-white rounded-lg shadow-lg">
<SearchItems />
</Search.ScrollableArea>
</Search.List>
) : null
)}
</Search.ResultsData>
</Search.ResultsFetcher>
);
- Create the search items component to render individual results:
import { Search, useSearchContext } from '@xweather/maps-ui-sdk';
const SearchItems = () => {
const { currentResults, activeDescendantId } = useSearchContext();
return currentResults.map((result, index) => (
<>
{index > 0 && (
<Search.Divider className="border-slate-300" />
)}
<Search.Item item={result}>
<Search.ItemButton
className={`px-4 py-1.5 text-sm ${
activeDescendantId === result.trackingId
? 'text-white bg-[#3ABBA3]'
: 'text-black'
}`}
/>
</Search.Item>
</>
));
};
The activeDescendantId
matches the trackingId
of the currently active/focused result, allowing you to style items based on keyboard navigation or hover state. This implements the ARIA combobox pattern (opens in a new tab) for accessibility.
- Finally, combine everything into a
LocationSearch
component:
import { Search } from '@xweather/maps-ui-sdk';
import { type WeatherPlaceSearchResult } from '@xweather/maps-ui-sdk/dist/types/search';
const handleSelectResult = (result: WeatherPlaceSearchResult) => {
const { lat, long } = result.loc;
// You can use these coordinates to update your map view
// See the 'Add a map controller with controls' example for setting up the map
};
export const LocationSearch = () => (
<Search
onSelectResult={handleSelectResult}
searchGroups={['places']} // Can also include 'stations' and 'recent'
resultFormatter={formatResult}
>
<Search.FocusArea className="w-80">
<SearchBar />
<SearchResults />
</Search.FocusArea>
</Search>
);
The searchGroups
prop can accept an array of different search types:
'places'
: Location search results'stations'
: Weather station results'recent'
: Recently viewed locations
Combine this with the Map Controller example to see a complete example of integrating this search component with a map.