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:
| Layout | Window | What it is |
|---|---|---|
fullscreen | compact | Covers the screen. No corner. Pads the top and bottom safe-area insets. |
docked | medium and wider | A 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
| Prop | What it changes |
|---|---|
containerColor | The surface background. Default surfaceContainerHigh. |
contentColor | The input text and the header icons. |
inputStyle | The TextInput element. |
style | The surface. A docked view reads maxHeight from it. |
inputProps | Other TextInput props, such as autoCapitalize. |
Specification
| Part | MD3 token | Value |
|---|---|---|
| Container | color | surfaceContainerHigh |
| Container | shape | full screen: none; docked: extra-large corner |
| Container | elevation | level 0 |
| Header | height | 72dp |
| Divider | color | outline |
| Back button | color | onSurface |
| Trailing icons | color | onSurfaceVariant |
| Input text | type, color | bodyLarge, onSurface |
| Placeholder | type, color | bodyLarge, onSurfaceVariant |
| Docked view | height | 240dp minimum, two thirds of the window maximum |
| Docked view | width | the 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. SetaccessibilityLabelto use a different name. - The header is a
searchlandmark on web, and the input has thesearchboxrole. - 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,
accessibilityViewIsModalkeeps the screen reader inside the view. - The back button, the clear button, and each action need a name. The defaults are
BackandClear search;actionsentries require anaccessibilityLabel. - The area outside a docked view is a button named
dismissAccessibilityLabel,Close searchby default.
Props
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
visible | boolean | - | Yes | Whether the view is open. The view mounts when this becomes `true`. |
onDismiss | () => void | - | Yes | Called 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. |
layout | enum | auto | No | The layout. `'auto'` is full screen on a compact window and docked on a wider one. |
anchor | RefObject<View> | - | No | The 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. |
value | string | - | No | Controlled query. Pair it with `onChangeText`. |
defaultValue | string | - | No | Initial query for the uncontrolled case. |
onChangeText | (text: string) => void | - | No | Called on every change to the query. |
placeholder | string | - | No | Placeholder of the input, and its accessible name by default. |
placeholderTextColor | string | theme.colors.onSurfaceVariant | No | Override the placeholder color. |
onSearch | (query: string) => void | - | No | Called with the current query when the user submits from the keyboard. |
backIcon | IconSource | arrow-left | No | Icon of the back button at the start of the header. |
backAccessibilityLabel | string | Back | No | Accessibility label of the back button. |
showClearButton | boolean | | No | Shows a clear button while the input holds text. |
clearButtonAccessibilityLabel | string | Clear search | No | Accessibility label of the clear button. |
onClear | () => void | - | No | Called after the clear button empties the input. |
actions | SearchBarAction[] | - | No | Icon buttons at the end of the header, such as a voice action. The clear button shows before them while the input holds text. |
dismissAccessibilityLabel | string | Close search | No | Accessibility label of the area outside a docked view, which closes the view on press. |
containerColor | string | theme.colors.surfaceContainerHigh | No | Override the container (background) color of the view. |
contentColor | string | - | No | Override the color of the input text and the header icons. |
inputStyle | StyleProp<TextStyle> | - | No | Additional style applied to the text input element. |
style | StyleProp<ViewStyle> | - | No | Style applied to the surface. A docked view reads `maxHeight` from it, the default is two thirds of the window height. |
inputProps | SearchViewInputProps | - | No | Other props of the `TextInput`, such as `autoCapitalize`. |
ref | Ref<TextInput> | - | No | Ref to the inner `TextInput`, for imperative `focus()`, `blur()` and `clear()` calls. |
children | ReactNode | - | No | The 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; } | - | No | Web 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 | - |