About
An accessible autocomplete input from ReUI with filtering, labels, clear and trigger controls, grouped people, asynchronous search results, and compact sizing.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds ReUI Autocomplete to your project and fits it to your theme.
Code
How ReUI Autocomplete is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client"
import {
type HTMLAttributes,
type ImgHTMLAttributes,
useMemo,
useState,
} from "react"
import {
Autocomplete,
AutocompleteCollection,
AutocompleteContent,
AutocompleteEmpty,
AutocompleteGroup,
AutocompleteGroupLabel,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
} from "@/components/ui/reui-autocomplete"
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"
function Avatar({ className = "", ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={`relative flex shrink-0 overflow-hidden rounded-full bg-muted ${className}`}
{...props}
/>
)
}
function AvatarImage(props: ImgHTMLAttributes<HTMLImageElement>) {
return <img className="absolute inset-0 size-full object-cover" {...props} />
}
function AvatarFallback({
className = "",
...props
}: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={`flex size-full items-center justify-center text-xs font-medium ${className}`}
{...props}
/>
)
}
export default function Pattern() {
const [value, setValue] = useState("")
const [open, setOpen] = useState(false)
const { contains } = AutocompletePrimitive.useFilter({ sensitivity: "base" })
const filteredItems = useMemo(() => {
if (!value) return groupedUsers
return groupedUsers
.map((group) => ({
...group,
items: (group.items || []).filter(
(item) =>
contains(item.name || "", value) ||
contains(item.group || "", value) ||
contains(item.position || "", value)
),
}))
.filter((group) => group.items && group.items.length > 0)
}, [value, contains])
return (
<div className="w-full max-w-xs">
<Autocomplete
items={filteredItems}
value={value}
onValueChange={setValue}
open={open}
onOpenChange={setOpen}
itemToStringValue={(item: unknown) => (item as User).name}
filter={null}
>
<AutocompleteInput placeholder="e.g. John, Developer, Marketing" />
{open && (
<AutocompleteContent className="pt-0">
{filteredItems.length === 0 ? (
<AutocompleteEmpty>No matching users found.</AutocompleteEmpty>
) : (
<AutocompleteList className="not-empty:py-0">
{(group: UserGroup) => (
<AutocompleteGroup
key={group.group}
items={group.items}
className="py-0"
>
<AutocompleteGroupLabel className="bg-background text-muted-foreground sticky top-0 z-10 me-1.5 py-2.5 text-xs font-medium">
{group.group}
</AutocompleteGroupLabel>
<AutocompleteCollection>
{(item: User) => (
<AutocompleteItem
key={item.id}
value={item}
className="flex items-center gap-2.5 rounded-lg"
>
<Avatar className="size-9">
<AvatarImage
src={item.avatar}
alt={item.name || "User"}
/>
<AvatarFallback>
{(item.name || "U")
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="min-w-0 flex-1">
<div className="truncate font-medium">
{item.name || "Unknown"}
</div>
<div className="text-muted-foreground truncate text-sm">
{item.position || "No position available"}
</div>
</div>
</AutocompleteItem>
)}
</AutocompleteCollection>
</AutocompleteGroup>
)}
</AutocompleteList>
)}
</AutocompleteContent>
)}
</Autocomplete>
</div>
)
}
interface User {
id: string
name: string
group: string
position: string
avatar: string
status: "Active" | "Inactive" | "Away"
}
interface UserGroup {
group: string
items: User[]
}
const usersData: User[] = [
// Development Team
{
id: "john-doe",
name: "John Doe",
group: "Development Team",
position: "Senior Frontend Developer",
avatar: "https://cdn.21st.dev/assets/mirror/b1/b1f6209ae26207ebe11c243a659f0e5e15a0a48232261ecf3c05211a40af2225.jpg",
status: "Active",
},
{
id: "jane-smith",
name: "Jane Smith",
group: "Development Team",
position: "Full Stack Developer",
avatar: "https://cdn.21st.dev/assets/mirror/e7/e7a0b30cb92ca533b2f8dbf57649e4b60129a9e84f3fc36d45b09e2dfcaec61d.jpg",
status: "Active",
},
{
id: "mike-wilson",
name: "Mike Wilson",
group: "Development Team",
position: "Backend Developer",
avatar: "https://cdn.21st.dev/assets/mirror/4c/4cff4f892ece6dca0865313df96f11ac30e11b6dcbf3b9a86bad86a3049aa6e1.jpg",
status: "Active",
},
{
id: "sarah-johnson",
name: "Sarah Johnson",
group: "Development Team",
position: "DevOps Engineer",
avatar: "https://cdn.21st.dev/assets/mirror/55/55d0cf713811843ffbd3412ee403668a82597bb83aabbc684a87f66c1fc962e4.jpg",
status: "Away",
},
{
id: "david-brown",
name: "David Brown",
group: "Development Team",
position: "Mobile Developer",
avatar: "https://cdn.21st.dev/assets/mirror/32/32afb68c9233445d08f7c4af3e781f648c6eeeb7dadeb5bdd341a003684d1c93.jpg",
status: "Active",
},
{
id: "lisa-garcia",
name: "Lisa Garcia",
group: "Development Team",
position: "UI/UX Developer",
avatar: "https://cdn.21st.dev/assets/mirror/7f/7f2f1b6a4c09f5092437fe960232360d1e2dcf7a198c8580f3c5478c7b2d9386.jpg",
status: "Active",
},
// Design Team
{
id: "alex-martinez",
name: "Alex Martinez",
group: "Design Team",
position: "Lead UX Designer",
avatar: "https://cdn.21st.dev/assets/mirror/f2/f25b1b7a6a351c0f748d81bf4fcaf8c5a2f8ed036563c2693d4c1ca3718d9d5d.jpg",
status: "Active",
},
{
id: "emma-davis",
name: "Emma Davis",
group: "Design Team",
position: "UI Designer",
avatar: "https://cdn.21st.dev/assets/mirror/41/417105f5784df0a25c3486becfe5c967d448e3c98b3c0231ef4ea0c59d27cb4b.jpg",
status: "Active",
},
{
id: "chris-taylor",
name: "Chris Taylor",
group: "Design Team",
position: "Product Designer",
avatar: "https://cdn.21st.dev/assets/mirror/62/6252a3b6790cbb48919cb8ea756a4e1ce829f3271a141731226871b3c3df9d6d.jpg",
status: "Active",
},
{
id: "olivia-anderson",
name: "Olivia Anderson",
group: "Design Team",
position: "Visual Designer",
avatar: "https://cdn.21st.dev/assets/mirror/54/54ebea0e1cad66565de28318ff2f512398bf5732f6f3f3fecea8ad4338b78778.jpg",
status: "Inactive",
},
// Marketing Team
{
id: "james-moore",
name: "James Moore",
group: "Marketing Team",
position: "Marketing Manager",
avatar: "https://cdn.21st.dev/assets/mirror/21/21d6722e3baa38cb075afdc599a60c8861440a6dea431e7a8ff43f86d05190b9.jpg",
status: "Active",
},
{
id: "sophia-white",
name: "Sophia White",
group: "Marketing Team",
position: "Content Marketing Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/20/20bb8458e0bb0345aae5ab6a975650d1210fdfc5721729b456f7342fc59b3113.jpg",
status: "Active",
},
{
id: "william-harris",
name: "William Harris",
group: "Marketing Team",
position: "Digital Marketing Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/56/56eb13218bfcbdfa4f6f950990d4666d616ad1f9e17777c976ccc79c781b68c6.jpg",
status: "Active",
},
{
id: "ava-martin",
name: "Ava Martin",
group: "Marketing Team",
position: "Social Media Manager",
avatar: "https://cdn.21st.dev/assets/mirror/4c/4c5eaf184e978fcf67bed792f0fa88543b664347c98727aa25da4c16e32eb367.jpg",
status: "Away",
},
// Sales Team
{
id: "ethan-thompson",
name: "Ethan Thompson",
group: "Sales Team",
position: "Sales Director",
avatar: "https://cdn.21st.dev/assets/mirror/e8/e86e44eaa9cb076c9d359973ce68af0e0cd85bb5dac2e72b259582941a57621b.jpg",
status: "Active",
},
{
id: "mia-garcia",
name: "Mia Garcia",
group: "Sales Team",
position: "Account Executive",
avatar: "https://cdn.21st.dev/assets/mirror/cc/cc6b757fbf1174ae601b39aa711d6dfcda1b236001a2f3a67c4293d73c9fd714.jpg",
status: "Active",
},
{
id: "noah-martinez",
name: "Noah Martinez",
group: "Sales Team",
position: "Sales Representative",
avatar: "https://cdn.21st.dev/assets/mirror/e8/e86e44eaa9cb076c9d359973ce68af0e0cd85bb5dac2e72b259582941a57621b.jpg",
status: "Active",
},
{
id: "isabella-rodriguez",
name: "Isabella Rodriguez",
group: "Sales Team",
position: "Business Development Manager",
avatar: "https://cdn.21st.dev/assets/mirror/1c/1c09f1ab9440e4f0922ad6c7494fb9efa1ae446ac5d39c4fb5843ed3fbc5c952.jpg",
status: "Active",
},
// Management Team
{
id: "lucas-lee",
name: "Lucas Lee",
group: "Management Team",
position: "CEO",
avatar: "https://cdn.21st.dev/assets/mirror/c4/c493b0a6d9a42ed0a102bcd31360d00491e23ac5cb4f7cbf8ae9c61f577ccccc.jpg",
status: "Active",
},
{
id: "charlotte-walker",
name: "Charlotte Walker",
group: "Management Team",
position: "CTO",
avatar: "https://cdn.21st.dev/assets/mirror/56/56cfb2a08032e82843ccac91504bbf42ababde4aea91bbacd9b683912cd8b21a.jpg",
status: "Active",
},
{
id: "benjamin-hall",
name: "Benjamin Hall",
group: "Management Team",
position: "VP of Engineering",
avatar: "https://cdn.21st.dev/assets/mirror/35/3560ff7cbc9e86c333fccefe248e3ea5cdade4e46f6b2fc85d84755896cb2e5a.jpg",
status: "Active",
},
{
id: "amelia-allen",
name: "Amelia Allen",
group: "Management Team",
position: "VP of Marketing",
avatar: "https://cdn.21st.dev/assets/mirror/aa/aa4787be04406deac036c92ff766754aa511214f00a4ee181ada4fc2c6622b6f.jpg",
status: "Active",
},
// Support Team
{
id: "henry-young",
name: "Henry Young",
group: "Support Team",
position: "Customer Success Manager",
avatar: "https://cdn.21st.dev/assets/mirror/ca/ca627d33f20754d25814a1d622a9f4837d56d5809c6fa7c14f2f2be7e3f36a05.jpg",
status: "Active",
},
{
id: "grace-king",
name: "Grace King",
group: "Support Team",
position: "Technical Support Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/aa/aaab4a0fbd8e2ad7a7ec4ccaa827918df0d6af1732227caa84d309cb49b45c21.jpg",
status: "Active",
},
{
id: "sebastian-wright",
name: "Sebastian Wright",
group: "Support Team",
position: "Customer Support Representative",
avatar: "https://cdn.21st.dev/assets/mirror/f6/f61f4b3793465be0aa4c3b577a6ccb95ae48ccfb751e7aaa6b5477aadc5255a8.jpg",
status: "Away",
},
{
id: "lily-lopez",
name: "Lily Lopez",
group: "Support Team",
position: "Help Desk Technician",
avatar: "https://cdn.21st.dev/assets/mirror/f9/f90a38d15ecefa1704387dfba523d1c3e78b6344bcee586e0378e6af5e5f7cd0.jpg",
status: "Active",
},
]
function groupUsers(users: User[]): UserGroup[] {
const groups: { [key: string]: User[] } = {}
users.forEach((item) => {
;(groups[item.group] ??= []).push(item)
})
// Sort by status within each group (Active first, then Away, then Inactive)
Object.keys(groups).forEach((group) => {
groups[group].sort((a, b) => {
const statusOrder = { Active: 0, Away: 1, Inactive: 2 }
return statusOrder[a.status] - statusOrder[b.status]
})
})
const order = [
"Management Team",
"Development Team",
"Design Team",
"Marketing Team",
"Sales Team",
"Support Team",
]
return order.map((group) => ({ group, items: groups[group] ?? [] }))
}
const groupedUsers: UserGroup[] = groupUsers(usersData)








