Skip to main content

SearchView

The search view is the panel that opens from a search bar and shows suggestions or results. Follows the Material Design 3 Search specification.

MD3 Search has two parts: the search bar and the search view. SearchBar is the bar. This component is the view. It renders through Portal, so your app must be wrapped in a PortalHost once at the root. It claims the PORTAL_LAYERS.dialog layer.

Usage​

import { List, ListItem, PortalHost, SearchBar, SearchView } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { useRef, useState } from 'react'
import { View } from 'react-native'

const SUGGESTIONS = ['Hiking trails', 'Weekend weather', 'Trail shoes']

function Screen() {
const [open, setOpen] = useState(false)
const [query, setQuery] = useState('')
const anchorRef = useRef(null)
return (
<View style={{ flex: 1, padding: 16 }}>
<View ref={anchorRef}>
<SearchBar
placeholder="Search trails"
value={query}
onChangeText={setQuery}
onPress={() => setOpen(true)}
/>
</View>
<SearchView
visible={open}
onDismiss={() => setOpen(false)}
anchor={anchorRef}
placeholder="Search trails"
value={query}
onChangeText={setQuery}
onSearch={() => setOpen(false)}
>
<List>
{SUGGESTIONS.filter((s) => s.toLowerCase().includes(query.toLowerCase())).map((s) => (
<ListItem key={s} headlineText={s} onPress={() => { setQuery(s); setOpen(false) }} />
))}
</List>
</SearchView>
</View>
)
}

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<PortalHost>
<Screen />
</PortalHost>
</ThemeProvider>
</SafeAreaProvider>
)
}

The view opens when visible becomes true, and the input takes focus. The header holds a back button, the input, a clear button while the input holds text, and the actions. children fills the rest of the view. Put a scrollable list there when it can grow past the view.

The view asks to close through onDismiss. It does so from the back button, from the Android back button, from Escape on web, and from a press outside a docked view. Set visible to false in response. The view does not close itself.

The query is controlled with value and onChangeText, or uncontrolled with defaultValue. Share the state with the bar, as above, so the bar shows the query after the view closes.

Give the bar onPress, not onFocus, to open the view. With onPress the bar is a button, and the view returns focus to it on close. A bar that opens the view on focus reopens it at that moment, because the returned focus fires onFocus again.

Layout​

MD3 gives the view two layouts, and layout="auto" (the default) picks one from the breakpoint:

LayoutWindowWhat it is
fullscreencompactCovers the screen. No corner. Pads the top and bottom safe-area insets.
dockedmedium and widerA panel with the extra-large corner. At least 240dp tall, at most two thirds of the window. A press outside it closes it.

Set layout to force one.

Anchor​

A docked view opens over the bar. Pass the bar's wrapping View as anchor, and the view takes its left edge, top edge and width, so the header covers the bar. Without an anchor, a docked view sits at the top of the screen, centered, 720dp wide at most.

The view measures the anchor when it opens and when the window resizes. The surface mounts after the measurement, so it never shows at the wrong place.

Colors and overrides​

PropWhat it changes
containerColorThe surface background. Default surfaceContainerHigh.
contentColorThe input text and the header icons.
inputStyleThe TextInput element.
styleThe surface. A docked view reads maxHeight from it.
inputPropsOther TextInput props, such as autoCapitalize.

Specification​

PartMD3 tokenValue
ContainercolorsurfaceContainerHigh
Containershapefull screen: none; docked: extra-large corner
Containerelevationlevel 0
Headerheight72dp
Dividercoloroutline
Back buttoncoloronSurface
Trailing iconscoloronSurfaceVariant
Input texttype, colorbodyLarge, onSurface
Placeholdertype, colorbodyLarge, onSurfaceVariant
Docked viewheight240dp minimum, two thirds of the window maximum
Docked viewwidththe anchor, or 720dp maximum

Values are from Compose SearchViewTokens and SearchBar.kt. MD3 Expressive adds a "contained" style, which the component does not build.

Accessibility​

  • The surface is a modal dialog, named by the placeholder. Set accessibilityLabel to use a different name.
  • The header is a search landmark on web, and the input has the searchbox role.
  • On web, focus moves to the input on open, Tab stays inside the view, Escape closes it, and focus returns to the control that opened it on close.
  • On native, accessibilityViewIsModal keeps the screen reader inside the view.
  • The back button, the clear button, and each action need a name. The defaults are Back and Clear search; actions entries require an accessibilityLabel.
  • The area outside a docked view is a button named dismissAccessibilityLabel, Close search by default.

Props​

PropTypeDefaultRequiredDescription
visibleboolean-YesWhether the view is open. The view mounts when this becomes `true`.
onDismiss() => void-YesCalled when the view asks to close: the back button, the Android back button, Escape on web, or a press outside a docked view. Set `visible` to `false` in response.
layoutenumautoNoThe layout. `'auto'` is full screen on a compact window and docked on a wider one.
anchorRefObject<View>-NoThe view the docked layout opens from, usually a `View` around the `SearchBar`. The docked view takes its left edge, top edge and width, so the header covers the bar. Without it, a docked view sits at the top of the screen, centered, 720 dp wide at most.
valuestring-NoControlled query. Pair it with `onChangeText`.
defaultValuestring-NoInitial query for the uncontrolled case.
onChangeText(text: string) => void-NoCalled on every change to the query.
placeholderstring-NoPlaceholder of the input, and its accessible name by default.
placeholderTextColorstringtheme.colors.onSurfaceVariantNoOverride the placeholder color.
onSearch(query: string) => void-NoCalled with the current query when the user submits from the keyboard.
backIconIconSourcearrow-leftNoIcon of the back button at the start of the header.
backAccessibilityLabelstringBackNoAccessibility label of the back button.
showClearButtonbooleanNoShows a clear button while the input holds text.
clearButtonAccessibilityLabelstringClear searchNoAccessibility label of the clear button.
onClear() => void-NoCalled after the clear button empties the input.
actionsSearchBarAction[]-NoIcon buttons at the end of the header, such as a voice action. The clear button shows before them while the input holds text.
dismissAccessibilityLabelstringClose searchNoAccessibility label of the area outside a docked view, which closes the view on press.
containerColorstringtheme.colors.surfaceContainerHighNoOverride the container (background) color of the view.
contentColorstring-NoOverride the color of the input text and the header icons.
inputStyleStyleProp<TextStyle>-NoAdditional style applied to the text input element.
styleStyleProp<ViewStyle>-NoStyle applied to the surface. A docked view reads `maxHeight` from it, the default is two thirds of the window height.
inputPropsSearchViewInputProps-NoOther props of the `TextInput`, such as `autoCapitalize`.
refRef<TextInput>-NoRef to the inner `TextInput`, for imperative `focus()`, `blur()` and `clear()` calls.
childrenReactNode-NoThe body below the header: suggestions, recent queries, or results. It fills the rest of the view. Put a scrollable list here when it can grow past the view.
hrefAttrs{ target?: string; rel?: string; download?: string | boolean; }-NoWeb only. The `target`, `rel` and `download` of the `<a>` that a view with an `href` renders, for example under Expo Router's `<Link asChild>`. `target: '_blank'` opens the link in a new tab. Native ignores it.
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-