AniUI

SearchBar

A search input with icon, clear button, and optional cancel action.

Web preview — components render natively on iOS & Android
import { SearchBar } from "@/components/ui/search-bar";

function MyScreen() {
  const [query, setQuery] = useState("");
  return (
    <SearchBar
      value={query}
      onChangeText={setQuery}
      onClear={() => setQuery("")}
    />
  );
}

Installation#

npx @aniui/cli add search-bar

Usage#

search-screen.tsx
import { SearchBar } from "@/components/ui/search-bar";

function MyScreen() {
  const [query, setQuery] = useState("");
  return (
    <SearchBar
      value={query}
      onChangeText={setQuery}
      onClear={() => setQuery("")}
    />
  );
}

Sizes#

Three sizes to fit different layouts.

Web preview — components render natively on iOS & Android
<SearchBar size="sm" placeholder="Small search..." />
<SearchBar size="md" placeholder="Medium search..." />
<SearchBar size="lg" placeholder="Large search..." />

With Cancel Button#

Show a cancel button when the search bar is focused (iOS pattern).

Web preview — components render natively on iOS & Android
const [query, setQuery] = useState("");
const [focused, setFocused] = useState(false);
<SearchBar
  value={query}
  onChangeText={setQuery}
  onClear={() => setQuery("")}
  onFocus={() => setFocused(true)}
  showCancel={focused}
  onCancel={() => { setQuery(""); setFocused(false); }}
/>

Custom Icon#

Pass any React element via the icon prop. The icon size adjusts automatically based on the size variant (sm: 14, md: 16, lg: 20).

custom-icon.tsx
import { Ionicons } from "@expo/vector-icons";

<SearchBar
  icon={<Ionicons name="search" size={18} color="#71717a" style={{ marginRight: 8 }} />}
  value={query}
  onChangeText={setQuery}
  onClear={() => setQuery("")}
/>

Props#

PropTypeDefault
size
"sm" | "md" | "lg"
"md"
value
string
onChangeText
(text: string) => void
icon
React.ReactNode
search icon
onClear
() => void
showCancel
boolean
false
onCancel
() => void
placeholder
string
"Search..."
className
string

Accessibility#

  • TextInput with accessibilityRole and search icon.
  • Clear button has accessibilityLabel for screen readers.

Source#

components/ui/search-bar.tsx
import React from "react";
import { View, TextInput, Pressable, Text } from "react-native";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const searchBarVariants = cva(
  "flex-row items-center rounded-lg bg-muted px-3 min-h-12",
  {
    variants: {
      size: {
        sm: "min-h-10 px-2.5",
        md: "min-h-12 px-3",
        lg: "min-h-14 px-4",
      },
    },
    defaultVariants: { size: "md" },
  }
);

const iconSizes = { sm: 14, md: 16, lg: 20 } as const;

export interface SearchBarProps
  extends Omit<React.ComponentPropsWithoutRef<typeof TextInput>, "placeholderTextColor">,
    VariantProps<typeof searchBarVariants> {
  className?: string;
  icon?: React.ReactNode;
  onClear?: () => void;
  showCancel?: boolean;
  onCancel?: () => void;
}
export function SearchBar({ size = "md", className, value, icon, onClear, showCancel, onCancel, ...props }: SearchBarProps) {
  const iconSize = iconSizes[size ?? "md"];
  return (
    <View className="flex-row items-center gap-2">
      <View className={cn(searchBarVariants({ size }), className)}>
        <View className="mr-2">
          {icon ?? <Text style={{ fontSize: iconSize, color: "#71717a" }}>\u2315</Text>}
        </View>
        <TextInput
          className="flex-1 text-base text-foreground p-0"
          placeholderTextColor="#71717a"
          placeholder="Search..."
          value={value}
          accessibilityRole="search"
          {...props}
        />
        {value ? (
          <Pressable onPress={onClear} className="ml-1 h-5 w-5 items-center justify-center rounded-full bg-muted-foreground/20" accessible={true} accessibilityRole="button" accessibilityLabel="Clear search">
            <Text className="text-xs text-muted-foreground">\u2715</Text>
          </Pressable>
        ) : null}
      </View>
      {showCancel && (
        <Pressable onPress={onCancel} accessible={true} accessibilityRole="button">
          <Text className="text-base text-primary">Cancel</Text>
        </Pressable>
      )}
    </View>
  );
}