SearchBar

The query field at the top of a list — clear, Cancel, debounced change reporting and filter tokens.

Open Source

Installation

SearchBar ships with the library — install it once, then import:

import { SearchBar } from "@delacour/react-native-ui/search-bar";

Or copy the source into your project, to own and edit it:

bunx delacour@latest add search-bar

Usage

app/index.tsx
import { SearchBar } from "@/components/ui/search-bar";
import { useState } from "react";
import { View } from "react-native";

export default function Example() {
  const [query, setQuery] = useState("");

  return (
    <View className="flex-1 p-4">
      <SearchBar onValueChange={setQuery} value={query} />
    </View>
  );
}

Debounce and submit

onDebouncedChange fires debounce ms after the last keystroke, at once on the search key, and with "" at once on clear or Cancel.

Debounced filter list
<SearchBar
  debounce={300}
  onDebouncedChange={runSearch}
  onSubmit={runSearch}
  onValueChange={setQuery}
  value={query}
/>

Cancel modes

cancel is focus by default; always keeps the action on screen and never leaves the field the full width.

Cancel modes
<SearchBar cancel="focus" />
<SearchBar cancel="always" cancelLabel="Done" />
<SearchBar cancel="never" />

Custom Cancel

Pass a SearchBar.Cancel as the child to change its label, variant or content; the bar still decides when it shows.

<SearchBar cancel="always">
  <SearchBar.Cancel variant="link">Close</SearchBar.Cancel>
</SearchBar>

Loading

isLoading swaps the clear button for a spinner and reports the field as busy.

<SearchBar isLoading={isSearching} onValueChange={setQuery} value={query} />

Tokens

Active filters sit inside the box as chips; Backspace on an empty field removes the last one.

Loading and tokens
Loading and tokens
const [filters, setFilters] = useState([
  { id: "open", label: "Open", icon: IconTag },
  { id: "this-week", label: "This week" },
]);

<SearchBar
  onTokenRemove={(token) => setFilters((current) => current.filter((entry) => entry.id !== token.id))}
  tokens={filters}
/>;

Results on focus

onFocusChange is where a screen swaps its content for results — the bar is a field, not a results surface.

const [isSearching, setSearching] = useState(false);

<View className="flex-1">
  <SearchBar onFocusChange={setSearching} onValueChange={setQuery} value={query} />
  {isSearching ? <Results query={query} /> : <Browse />}
</View>;

Inside a list header

Render it as the list's header, and pin it with the list's own sticky header index.

<FlatList
  data={rows}
  keyboardShouldPersistTaps="handled"
  ListHeaderComponent={<SearchBar onValueChange={setQuery} value={query} />}
  renderItem={renderRow}
  stickyHeaderIndices={[0]}
/>

Inside a BottomSheet

Spread useBottomSheetInput() across inputRef, onFocus and onBlur, and the sheet lifts for the keyboard.

const sheetInput = useBottomSheetInput();

<SearchBar
  inputRef={sheetInput.ref}
  onBlur={sheetInput.onBlur}
  onFocus={sheetInput.onFocus}
  onValueChange={setQuery}
  value={query}
/>;

Sizes, shapes and variants

The box is Input's — three sizes and three variants — drawn as a pill or with the field's own corner.

Sizes, shapes and variants
Sizes, shapes and variants
<SearchBar size="sm" />
<SearchBar size="lg" variant="primary" />
<SearchBar shape="rounded" />

Accessibility

The field has the search role and takes its name from an enclosing Field.Label, else the placeholder; clear and Cancel are labelled buttons, and a hidden Cancel is out of the accessibility tree.

<Field>
  <Field.Label>Search orders</Field.Label>
  <SearchBar clearAccessibilityLabel="Clear order search" />
</Field>

API Reference

SearchBarProps

Everything Input accepts is accepted and reaches the field, except multiline; className lands on the row holding the field and Cancel.

Prop

Type

SearchBar.Cancel

A ghost Button at the field's size that cancels the search after its own onPress. Extends ButtonProps except size.

Prop

Type

useSearchBar

Reads the enclosing bar's value, isFocused, isDisabled, isLoading, size and isCancelVisible, and its clear() and cancel() actions, for a custom child. Throws outside a <SearchBar>.

On this page