Headless React autocomplete / multiselect component with optional styles.
Lightweight, customizable, type-safe, and dependency-free — perfect for design systems and modern React apps.
- 🧠 Headless-first — full control over rendering and styling
- 🎨 Optional built-in styles
- 🔍 Custom filtering with
FilterFn - 🧩 Multi-select support
- 🔧 Custom icons (React components or elements)
- 📘 TypeScript-first
- 📦 Zero dependencies (besides React)
- 🪶 Small bundle size
npm install @zhele/autocomplete(Optional) import built-in styles:
import "@zhele/autocomplete/style";import { useState } from "react";
import { AutocompleteJ } from "@zhele/autocomplete";
import "@zhele/autocomplete/style";
type Item = { id: number; label: string };
const items: Item[] = [
{ id: 1, label: "Apple" },
{ id: 2, label: "Banana" },
{ id: 3, label: "Orange" }
];
export function Example() {
const [selected, setSelected] = useState<Item[]>([]);
const toggle = (item: Item) =>
setSelected((prev) =>
prev.some((i) => i.id === item.id)
? prev.filter((i) => i.id !== item.id)
: [...prev, item]
);
return (
<AutocompleteJ
items={items}
selectedItems={selected}
onToggleItem={toggle}
placeholder="Choose fruits..."
/>
);
}| Prop | Type | Required | Description |
|---|---|---|---|
items |
T[] |
✔️ | All available options |
selectedItems |
T[] |
✔️ | Currently selected items |
onToggleItem |
(item: T) => void |
✔️ | Toggle handler |
placeholder |
string |
— | Input placeholder |
filterFn |
FilterFn<T> |
— | Custom filtering function |
classes |
AutocompleteJClasses |
— | Override built-in classes |
icons |
AutocompleteJIcons |
— | Replace internal icons |
getOptionLabel |
(item: T) => string |
— | Custom label extractor |
export interface BaseItem {
id: string | number;
label?: string;
[key: string]: unknown;
}export type FilterFn<T extends BaseItem> =
(item: T, query: string) => boolean;Example:
const startsWith: FilterFn<Item> = (item, query) =>
item.label?.toLowerCase().startsWith(query.toLowerCase()) ?? false;<AutocompleteJ
...
classes={{
input: "my-input",
dropdown: "my-dropdown",
optionChecked: "my-option-checked"
}}
/>Available keys:
export type AutocompleteJClasses = Partial<{
root: string;
form: string;
input: string;
clearButton: string;
chevronButton: string;
dropdown: string;
option: string;
optionChecked: string;
optionCheckbox: string;
optionLabel: string;
emptyState: string;
}>;Supports both React components and React elements.
import { ChevronDown, ChevronUp, X } from "lucide-react";
<AutocompleteJ
...
icons={{
ClearIcon: <X size={24} color="red" />,
ChevronDownIcon: <ChevronUDown />,
ChevronUpIcon: <ChevronUp />
}}
/>Icon type:
export type IconJSlot = React.ElementType | React.ReactElement;Props passed to icon components:
type IconJCommonProps = {
size?: number;
color?: string;
className?: string;
};type Item = { id: number; label: string; category: string };
const getOptionLabel = (item: Item) =>
`${item.label} (${item.category})`;- Clean, predictable API
- Zero extra dependencies
- Excellent TypeScript DX
- Works great with:
- TailwindCSS
- MUI
- Mantine
- Chakra UI
- Custom design systems
- Headless-first: BYO styles and components
MIT © Zheleznikov