extend
uiautocompletesource: ok · hit
Autocomplete
An autocomplete input built on Base UI primitives.
npx shadcn add "https://ui.extend.ai/r/autocomplete.json"free
No selection tags yet — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightui-primitives@base-ui/react@^1.4.1
Registry dependencies
inputscroll-areautils
Files1 · source cached 10/11/2026, 4:38:41 AM
registry/new-york-v4/ui/autocomplete.tsx
"use client"
import type React from "react"
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"
import { cn } from "@/lib/utils"
import { Input } from "@/components/ui/input"
import { ScrollArea } from "@/components/ui/scroll-area"
import { IconPlaceholder } from "@/components/icon-placeholder"
export const Autocomplete: typeof AutocompletePrimitive.Root =
AutocompletePrimitive.Root
export function AutocompleteInput({
className,
showTrigger = false,
showClear = false,
startAddon,
size,
triggerProps,
clearProps,
...props
}: Omit<AutocompletePrimitive.Input.Props, "size"> & {
showTrigger?: boolean
showClear?: boolean
startAddon?: React.ReactNode
size?: "sm" | "default" | "lg" | number
ref?: React.Ref<HTMLInputElement>
triggerProps?: AutocompletePrimitive.Trigger.Props
clearProps?: AutocompletePrimitive.Clear.Props
}): React.ReactElement {
const sizeValue = (size ?? "default") as "sm" | "default" | "lg" | number
return (
<AutocompletePrimitive.InputGroup
className="relative w-full text-foreground not-has-[>*.w-full]:w-fit has-disabled:opacity-64"
data-slot="autocomplete-input-group"
>
{startAddon && (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-y-0 start-px z-10 flex items-center ps-[calc(--spacing(3)-1px)] opacity-80 has-[+[data-size=sm]]:ps-[calc(--spacing(2.5)-1px)] [&_svg]:-mx-0.5 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4"
data-slot="autocomplete-start-addon"
>
{startAddon}
</div>
)}
<AutocompletePrimitive.Input
className={cn(
startAddon &&
"*:data-[slot=autocomplete-input]:ps-[calc(--spacing(8.5)-1px)] data-[size=sm]:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(7.5)-1px)] sm:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(8)-1px)] sm:data-[size=sm]:*:data-[slot=autocomplete-input]:ps-[calc(--spacing(7)-1px)]",
sizeValue === "sm"
? "has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=autocomplete-input]:pe-6.5"
: "has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=autocomplete-input]:pe-7",
className
)}
data-slot="autocomplete-input"
render={<Input nativeInput size={sizeValue} />}
{...props}
/>
{showTrigger && (
<AutocompleteTrigger
className={cn(
"absolute top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 transition-colors outline-none hover:opacity-100 has-[+[data-slot=autocomplete-clear]]:hidden sm:size-7 pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
sizeValue === "sm" ? "end-0" : "end-0.5"
)}
{...triggerProps}
>
<AutocompletePrimitive.Icon data-slot="autocomplete-icon">
<IconPlaceholder
lucide="ArrowUpDown"
tabler="IconArrowsUpDown"
hugeicons="ArrowUpDownIcon"
phosphor="ArrowsDownUpIcon"
remixicon="RiArrowUpDownLine"
/>
</AutocompletePrimitive.Icon>
</AutocompleteTrigger>
)}
{showClear && (
<AutocompleteClear
className={cn(
"absolute top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 transition-colors outline-none hover:opacity-100 has-[+[data-slot=autocomplete-clear]]:hidden sm:size-7 pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
sizeValue === "sm" ? "end-0" : "end-0.5"
)}
{...clearProps}
>
<IconPlaceholder
lucide="X"
tabler="IconX"
hugeicons="Cancel01Icon"
phosphor="XIcon"
remixicon="RiCloseLine"
/>
</AutocompleteClear>
)}
</AutocompletePrimitive.InputGroup>
)
}
export function AutocompletePopup({
className,
children,
side = "bottom",
sideOffset = 4,
alignOffset,
align = "start",
anchor,
portalProps,
...props
}: AutocompletePrimitive.Popup.Props & {
align?: AutocompletePrimitive.Positioner.Props["align"]
sideOffset?: AutocompletePrimitive.Positioner.Props["sideOffset"]
alignOffset?: AutocompletePrimitive.Positioner.Props["alignOffset"]
side?: AutocompletePrimitive.Positioner.Props["side"]
anchor?: AutocompletePrimitive.Positioner.Props["anchor"]
portalProps?: AutocompletePrimitive.Portal.Props
}): React.ReactElement {
return (
<AutocompletePrimitive.Portal {...portalProps}>
<AutocompletePrimitive.Positioner
align={align}
alignOffset={alignOffset}
anchor={anchor}
className="z-50 select-none"
data-slot="autocomplete-positioner"
side={side}
sideOffset={sideOffset}
>
<span
className={cn(
"relative flex max-h-full max-w-(--available-width) min-w-(--anchor-width) origin-(--transform-origin) rounded-lg border bg-popover shadow-lg/5 transition-[scale,opacity] not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
className
)}
>
<AutocompletePrimitive.Popup
className="flex max-h-[min(var(--available-height),23rem)] flex-1 flex-col text-foreground"
data-slot="autocomplete-popup"
{...props}
>
{children}
</AutocompletePrimitive.Popup>
</span>
</AutocompletePrimitive.Positioner>
</AutocompletePrimitive.Portal>
)
}
export function AutocompleteItem({
className,
children,
...props
}: AutocompletePrimitive.Item.Props): React.ReactElement {
return (
<AutocompletePrimitive.Item
className={cn(
"flex min-h-8 cursor-default items-center rounded-sm px-2 py-1 text-base outline-none select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground sm:min-h-7 sm:text-sm data-disabled:pointer-events-none data-disabled:opacity-64",
className
)}
data-slot="autocomplete-item"
{...props}
>
{children}
</AutocompletePrimitive.Item>
)
}
export function AutocompleteSeparator({
className,
...props
}: AutocompletePrimitive.Separator.Props): React.ReactElement {
return (
<AutocompletePrimitive.Separator
className={cn("mx-2 my-1 h-px bg-border last:hidden", className)}
data-slot="autocomplete-separator"
{...props}
/>
)
}
export function AutocompleteGroup({
className,
...props
}: AutocompletePrimitive.Group.Props): React.ReactElement {
return (
<AutocompletePrimitive.Group
className={cn("[[role=group]+&]:mt-1.5", className)}
data-slot="autocomplete-group"
{...props}
/>
)
}
export function AutocompleteGroupLabel({
className,
...props
}: AutocompletePrimitive.GroupLabel.Props): React.ReactElement {
return (
<AutocompletePrimitive.GroupLabel
className={cn(
"px-2 py-1.5 text-xs font-medium text-muted-foreground",
className
)}
data-slot="autocomplete-group-label"
{...props}
/>
)
}
export function AutocompleteEmpty({
className,
...props
}: AutocompletePrimitive.Empty.Props): React.ReactElement {
return (
<AutocompletePrimitive.Empty
className={cn(
"text-center text-base text-muted-foreground not-empty:p-2 sm:text-sm",
className
)}
data-slot="autocomplete-empty"
{...props}
/>
)
}
export function AutocompleteRow({
className,
...props
}: AutocompletePrimitive.Row.Props): React.ReactElement {
return (
<AutocompletePrimitive.Row
className={className}
data-slot="autocomplete-row"
{...props}
/>
)
}
export function AutocompleteValue({
...props
}: AutocompletePrimitive.Value.Props): React.ReactElement {
return (
<AutocompletePrimitive.Value data-slot="autocomplete-value" {...props} />
)
}
export function AutocompleteList({
className,
...props
}: AutocompletePrimitive.List.Props): React.ReactElement {
return (
<ScrollArea scrollbarGutter scrollFade>
<AutocompletePrimitive.List
className={cn(
"not-empty:scroll-py-1 not-empty:p-1 in-data-has-overflow-y:pe-3",
className
)}
data-slot="autocomplete-list"
{...props}
/>
</ScrollArea>
)
}
export function AutocompleteClear({
className,
...props
}: AutocompletePrimitive.Clear.Props): React.ReactElement {
return (
<AutocompletePrimitive.Clear
className={cn(
"absolute end-0.5 top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 transition-[color,background-color,box-shadow,opacity] outline-none hover:opacity-100 sm:size-7 pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
className
)}
data-slot="autocomplete-clear"
{...props}
>
<IconPlaceholder
lucide="X"
tabler="IconX"
hugeicons="Cancel01Icon"
phosphor="XIcon"
remixicon="RiCloseLine"
/>
</AutocompletePrimitive.Clear>
)
}
export function AutocompleteStatus({
className,
...props
}: AutocompletePrimitive.Status.Props): React.ReactElement {
return (
<AutocompletePrimitive.Status
className={cn(
"px-3 py-2 text-xs font-medium text-muted-foreground empty:m-0 empty:p-0",
className
)}
data-slot="autocomplete-status"
{...props}
/>
)
}
export function AutocompleteCollection({
...props
}: AutocompletePrimitive.Collection.Props): React.ReactElement {
return (
<AutocompletePrimitive.Collection
data-slot="autocomplete-collection"
{...props}
/>
)
}
export function AutocompleteTrigger({
className,
children,
...props
}: AutocompletePrimitive.Trigger.Props): React.ReactElement {
return (
<AutocompletePrimitive.Trigger
className={className}
data-slot="autocomplete-trigger"
{...props}
>
{children}
</AutocompletePrimitive.Trigger>
)
}
export const useAutocompleteFilter: typeof AutocompletePrimitive.useFilter =
AutocompletePrimitive.useFilter
export { AutocompletePrimitive }