- Accordion
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Checkbox
- Combobox
- Container
- CurrencyInput
- DistributionSlider
- Drawer
- Dropdown
- Grid
- Heading
- Image
- Input
- InputGroup
- Label
- Logo
- MapPin
- Modal
- NativeSelect
- NumberInput
- OtpInput
- PhoneInput
- Popover
- Progress
- PropertyCalendar
- RadioGroup
- RadioGroupCards
- ResponsiveModal
- ScrollArea
- SearchBar
- SearchBarFallback
- SearchInput
- Select
- Separator
- Spinner
- Switch
- Tabs
- Text
- Textarea
- Toast
- Toggle
- ToggleGroup
- Tooltip
SearchBar
A compound search bar component for location, dates, and guests selection.
pnpm add @wandercom/design-system-web
The SearchBar is a compound component that provides a flexible search interface. It consists of a collapsed bar with location trigger, date/guest buttons, and an expandable dropdown panel for location search.
import {
SearchBar,
SearchBarActionArea,
SearchBarButton,
SearchBarDesktop,
SearchBarLocationTrigger,
SearchBarRoot,
SearchBarSearchButton,
} from '@wandercom/design-system-web/ui/search-bar';
export function Example() {
return (
<SearchBarRoot
desktop={
<SearchBarDesktop>
<SearchBarLocationTrigger>Where?</SearchBarLocationTrigger>
<SearchBarButton>Anytime</SearchBarButton>
<SearchBarActionArea>
<SearchBarButton>Any guests</SearchBarButton>
<SearchBarSearchButton />
</SearchBarActionArea>
</SearchBarDesktop>
}
/>
);
}Use SearchBar for the full experience with built-in panels and labels:
import { SearchBar } from '@wandercom/design-system-web/ui/search-bar';
export function Example() {
return (
<SearchBar
labels={{
searchLabel: 'Search',
locationLabel: 'Where?',
}}
/>
);
}Use the location and onLocationChange props to manage location state externally, e.g. syncing with a map view.
import { SearchBar, type SearchBarLocation } from '@wandercom/design-system-web/ui/search-bar';
import { useState } from 'react';
export function ControlledLocationExample() {
const [location, setLocation] = useState<SearchBarLocation | null>(null);
return (
<SearchBar
location={location}
onLocationChange={setLocation}
onSearch={(values) => console.log(values)}
/>
);
}When location is omitted, the component manages its own state internally (uncontrolled mode, initialized from initialValues).
A complete search bar with location trigger, dates, guests, and search button.
The SearchBarPopover provides a dropdown for location search with sections and items using the Popover component.
import {
SearchBarPopover,
SearchBarPopoverContent,
SearchBarPopoverInput,
SearchBarPopoverItem,
SearchBarPopoverSection,
SearchBarPopoverTrigger,
SearchBarLocationTrigger,
} from '@wandercom/design-system-web/ui/search-bar';
import { useState } from 'react';
export function LocationPopover() {
const [open, setOpen] = useState(false);
return (
<SearchBarPopover open={open} onOpenChange={setOpen}>
<SearchBarPopoverTrigger asChild>
<SearchBarLocationTrigger active={open}>Where?</SearchBarLocationTrigger>
</SearchBarPopoverTrigger>
<SearchBarPopoverContent>
<SearchBarPopoverInput placeholder="Search locations..." />
<SearchBarPopoverSection label="Recent searches">
<SearchBarPopoverItem
title="Los Angeles"
subtitle="Los Angeles, California"
/>
<SearchBarPopoverItem
title="Joshua Tree"
subtitle="California, United States"
/>
</SearchBarPopoverSection>
</SearchBarPopoverContent>
</SearchBarPopover>
);
}Use the asChild prop to render custom elements while maintaining functionality.
import { SearchBarLocationTrigger } from '@wandercom/design-system-web/ui/search-bar';
import { Popover, PopoverTrigger } from '@wandercom/design-system-web/ui/popover';
export function ComposedTrigger() {
return (
<Popover>
<SearchBarLocationTrigger asChild>
<PopoverTrigger>Where?</PopoverTrigger>
</SearchBarLocationTrigger>
</Popover>
);
}The SearchBarDatePopoverContent provides a date range picker with calendar, mode toggle (Dates/Flexible), and flexibility options. Click the "Anytime" button in the example above to see the date popover.
import {
SearchBarButton,
SearchBarDatePopoverCalendar,
SearchBarDatePopoverContent,
SearchBarDatePopoverDuration,
SearchBarDatePopoverFlexibility,
SearchBarDatePopoverHeader,
SearchBarDatePopoverMonthGrid,
SearchBarPopover,
SearchBarPopoverTrigger,
type SearchBarDateDuration,
type SearchBarDateMonth,
} from '@wandercom/design-system-web/ui/search-bar';
import { useState } from 'react';
import type { DateRange } from 'react-day-picker';
export function DatePopover() {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState<'dates' | 'flexible'>('dates');
const [dateRange, setDateRange] = useState<DateRange | undefined>();
const [flexibility, setFlexibility] = useState<'exact' | '1' | '2' | '3' | '7'>('exact');
const [duration, setDuration] = useState<SearchBarDateDuration>('weekend');
const [selectedMonths, setSelectedMonths] = useState<SearchBarDateMonth[]>([]);
return (
<SearchBarPopover open={open} onOpenChange={setOpen}>
<SearchBarPopoverTrigger asChild>
<SearchBarButton>Anytime</SearchBarButton>
</SearchBarPopoverTrigger>
<SearchBarDatePopoverContent>
<SearchBarDatePopoverHeader
mode={mode}
onModeChange={setMode}
onClear={() => {
setDateRange(undefined);
setSelectedMonths([]);
}}
/>
{mode === 'dates' && (
<>
<SearchBarDatePopoverCalendar
selected={dateRange}
onSelect={setDateRange}
disabled={{ before: new Date() }}
/>
<SearchBarDatePopoverFlexibility
value={flexibility}
onValueChange={setFlexibility}
disabled={!dateRange?.from || !dateRange?.to}
/>
</>
)}
{mode === 'flexible' && (
<div className="flex flex-col gap-6 px-7 py-7">
<SearchBarDatePopoverDuration value={duration} onValueChange={setDuration} />
<SearchBarDatePopoverMonthGrid selected={selectedMonths} onSelect={setSelectedMonths} />
</div>
)}
</SearchBarDatePopoverContent>
</SearchBarPopover>
);
}The SearchBarGuestsPopoverContent provides a stepper interface for selecting guests and pets. Click the "Who?" button in the example above to see the guests popover.
import {
SearchBarButton,
SearchBarGuestsPopoverContent,
SearchBarGuestsPopoverRow,
SearchBarPopover,
SearchBarPopoverTrigger,
} from '@wandercom/design-system-web/ui/search-bar';
import { useState } from 'react';
export function GuestsPopover() {
const [open, setOpen] = useState(false);
const [guests, setGuests] = useState(1);
const [pets, setPets] = useState(0);
return (
<SearchBarPopover open={open} onOpenChange={setOpen}>
<SearchBarPopoverTrigger asChild>
<SearchBarButton>Any guests</SearchBarButton>
</SearchBarPopoverTrigger>
<SearchBarGuestsPopoverContent>
<SearchBarGuestsPopoverRow
label="Guests"
value={guests}
onChange={setGuests}
min={1}
max={16}
/>
<SearchBarGuestsPopoverRow
label="Pets"
value={pets}
onChange={setPets}
max={10}
/>
</SearchBarGuestsPopoverContent>
</SearchBarPopover>
);
}className?:
locations?:
units?:
recentSearches?:
suggestedRegions?:
initialValues?:
minDate?:
labels?:
onSearch?:
location?:
onLocationChange?:
onLocationSelect?:
onLocationQueryChange?:
locationSuggestions?:
unitSuggestions?:
isLoadingSuggestions?:
filtersContent?:
filtersValue?:
onClearFilters?:
desktopFiltersContent?:
asChild?:
className?:
desktop?:
mobile?:
asChild?:
children?:
className?:
children:
activeSegment?:
onActiveSegmentChange?:
hasLocationValue?:
hasDatesValue?:
hasGuestsValue?:
active?:
asChild?:
className?:
label?:
placeholder?:
asChild?:
active?:
segment?:
className?:
label?:
placeholder?:
className?:
children:
className?:
children?:
Re-exported Radix Popover component for controlling open/closed state.
Re-exported PopoverTrigger for triggering the popover. Use with asChild prop.
Re-exported PopoverAnchor for custom anchor positioning.
Extends all PopoverContent props from Radix.
className?:
children:
align?:
sideOffset?:
placeholder?:
className?:
onClear?:
onSubmit?:
label?:
maxItems?:
className?:
children:
title:
subtitle?:
icon?:
image?:
selected?:
asChild?:
onSelect?:
navigateTo?:
className?:
Extends all PopoverContent props from Radix.
className?:
children:
align?:
sideOffset?:
mode?:
onModeChange?:
onClear?:
showClear?:
className?:
datesLabel?:
flexibleLabel?:
clearLabel?:
Extends most DayPickerProps from react-day-picker (excluding mode, numberOfMonths, and showOutsideDays).
selected?:
onSelect?:
endMonth?:
disabled?:
className?:
value?:
onValueChange?:
disabled?:
className?:
exactLabel?:
oneDayLabel?:
twoDaysLabel?:
threeDaysLabel?:
sevenDaysLabel?:
value?:
onValueChange?:
className?:
titleLabel?:
weekendLabel?:
weekLabel?:
selected?:
onSelect?:
monthCount?:
startDate?:
titleLabel?:
className?:
Extends all PopoverContent props from Radix.
className?:
children:
align?:
sideOffset?:
label:
value:
onChange:
min?:
max?:
className?:
className?:
children?:
open?:
onOpenChange?:
trigger?:
activeSegment?:
onActiveSegmentChange?:
hasLocationValue?:
hasDatesValue?:
hasGuestsValue?:
onSearch?:
footer?:
titleLabel?:
filtersLabel?:
filtersPlaceholder?:
filtersValue?:
filtersContent?:
onClearFilters?:
filtersTitleLabel?:
clearFiltersLabel?:
showResultsLabel?:
className?:
onSkip?:
showSkip?:
searchLabel?:
nextLabel?:
skipLabel?:
className?:
placeholder?:
location?:
dates?:
guests?:
section?:
label:
value?:
expanded?:
onClick?:
headerAction?:
autoFocus?:
className?:
children?:
className?:
children?:
A self-contained calendar experience for the mobile drawer, including a mode toggle (Dates/Flexible), date range calendar with day-of-week labels, flexibility selector, duration toggle, and month grid.
selected?:
onSelect?:
mode?:
onModeChange?:
flexibility?:
onFlexibilityChange?:
numberOfMonths?:
minDate?:
duration?:
onDurationChange?:
selectedMonths?:
onSelectedMonthsChange?:
labels?:
className?:
A lightweight static fallback for use with Suspense boundaries or initial server renders. See the SearchBar Fallback documentation for full details.
The SearchBar component includes proper accessibility features:
- Uses semantic button elements for interactive triggers
- Dividers are marked with
aria-hidden="true" - Panel items support
aria-selectedfor selection state - All interactive elements are keyboard accessible
- Focus states are visible with proper styling
- Screen reader compatible with proper text labels
- Stepper buttons have descriptive
aria-labelattributes (e.g., "Increase guests", "Decrease pets") - Counter values use
aria-live="polite"to announce changes to screen readers - Disabled buttons are properly marked with
disabledattribute