Devie UI
Version: 2026-02-14

The Autocomplete component provides a text input with a dropdown of suggestions that filter as the user types. It extends Base UI's Autocomplete.

Autocomplete is best suited for free-form text input with suggestions (e.g., search fields, address inputs).

  • Unlike Select, the input value can be any custom text; it doesn't need to match an option.
  • If you need the user to select a value from a predefined list (like Select, but with filtering), you should consider the Combobox instead.

Installation

Here is the component implementation code that you can copy to your project:

// https://devie-ui.com/components/autocomplete
// https://base-ui.com/react/components/autocomplete
 
import { Autocomplete as BaseAutocomplete } from "@base-ui/react/autocomplete";
import clsx from "clsx";
import { ChevronDown, X } from "lucide-react";
import type React from "react";
import styles from "./Autocomplete.module.scss";
 
const Root = BaseAutocomplete.Root;
 
const Value = BaseAutocomplete.Value;
 
function Input({ className, ...props }: BaseAutocomplete.Input.Props) {
  return (
    <BaseAutocomplete.Input
      className={clsx(styles.input, className)}
      {...props}
    />
  );
}
 
function Trigger({
  className,
  render,
  ...props
}: BaseAutocomplete.Trigger.Props) {
  return (
    <BaseAutocomplete.Trigger
      className={clsx(!render && styles.trigger, className)}
      render={render}
      {...props}
    />
  );
}
 
function Icon({
  className,
  children,
  ...props
}: React.ComponentProps<typeof BaseAutocomplete.Icon>) {
  return (
    <BaseAutocomplete.Icon className={clsx(styles.icon, className)} {...props}>
      {children || <ChevronDown size={16} />}
    </BaseAutocomplete.Icon>
  );
}
 
function Clear({
  className,
  children,
  ...props
}: BaseAutocomplete.Clear.Props) {
  return (
    <BaseAutocomplete.Clear
      className={clsx(styles.clear, className)}
      {...props}
    >
      {children || <X size={16} />}
    </BaseAutocomplete.Clear>
  );
}
 
function List({ className, ...props }: BaseAutocomplete.List.Props) {
  return (
    <BaseAutocomplete.List
      className={clsx(styles.list, className)}
      {...props}
    />
  );
}
 
const Portal = BaseAutocomplete.Portal;
 
const Backdrop = BaseAutocomplete.Backdrop;
 
function Positioner({
  className,
  ...props
}: BaseAutocomplete.Positioner.Props) {
  return (
    <BaseAutocomplete.Positioner
      className={clsx(styles.positioner, className)}
      {...props}
    />
  );
}
 
function Popup({ className, ...props }: BaseAutocomplete.Popup.Props) {
  return (
    <BaseAutocomplete.Popup
      className={clsx(styles.popup, className)}
      {...props}
    />
  );
}
 
function Arrow({ className, ...props }: BaseAutocomplete.Arrow.Props) {
  return (
    <BaseAutocomplete.Arrow
      className={clsx(styles.arrow, className)}
      {...props}
    />
  );
}
 
function Status({ className, ...props }: BaseAutocomplete.Status.Props) {
  return (
    <BaseAutocomplete.Status
      className={clsx(styles.status, className)}
      {...props}
    />
  );
}
 
function Empty({ className, ...props }: BaseAutocomplete.Empty.Props) {
  return (
    <BaseAutocomplete.Empty
      className={clsx(styles.empty, className)}
      {...props}
    />
  );
}
 
const Collection = BaseAutocomplete.Collection;
 
function Row({ className, ...props }: BaseAutocomplete.Row.Props) {
  return (
    <BaseAutocomplete.Row className={clsx(styles.row, className)} {...props} />
  );
}
 
function Item({ className, ...props }: BaseAutocomplete.Item.Props) {
  return (
    <BaseAutocomplete.Item
      className={clsx(styles.item, className)}
      {...props}
    />
  );
}
 
function Group({ className, ...props }: BaseAutocomplete.Group.Props) {
  return (
    <BaseAutocomplete.Group
      className={clsx(styles.group, className)}
      {...props}
    />
  );
}
 
function GroupLabel({
  className,
  ...props
}: BaseAutocomplete.GroupLabel.Props) {
  return (
    <BaseAutocomplete.GroupLabel
      className={clsx(styles.groupLabel, className)}
      {...props}
    />
  );
}
 
function Separator({ className, ...props }: BaseAutocomplete.Separator.Props) {
  return (
    <BaseAutocomplete.Separator
      className={clsx(styles.separator, className)}
      {...props}
    />
  );
}
 
const useFilter = BaseAutocomplete.useFilter;
 
const Autocomplete = {
  Root,
  Value,
  Input,
  Trigger,
  Icon,
  Clear,
  List,
  Portal,
  Backdrop,
  Positioner,
  Popup,
  Arrow,
  Status,
  Empty,
  Collection,
  Row,
  Item,
  Group,
  GroupLabel,
  Separator,
  useFilter,
};
 
namespace Autocomplete {
  export namespace Root {
    export type Props<Value> = BaseAutocomplete.Root.Props<Value>;
    export type ChangeEventDetails = BaseAutocomplete.Root.ChangeEventDetails;
    export type ChangeEventReason = BaseAutocomplete.Root.ChangeEventReason;
  }
  export namespace Value {
    export type Props = BaseAutocomplete.Value.Props;
  }
  export namespace Input {
    export type Props = BaseAutocomplete.Input.Props;
  }
  export namespace Trigger {
    export type Props = BaseAutocomplete.Trigger.Props;
  }
  export namespace Icon {
    export type Props = React.ComponentProps<typeof BaseAutocomplete.Icon>;
  }
  export namespace Clear {
    export type Props = BaseAutocomplete.Clear.Props;
  }
  export namespace List {
    export type Props = BaseAutocomplete.List.Props;
  }
  export namespace Portal {
    export type Props = BaseAutocomplete.Portal.Props;
  }
  export namespace Backdrop {
    export type Props = BaseAutocomplete.Backdrop.Props;
  }
  export namespace Positioner {
    export type Props = BaseAutocomplete.Positioner.Props;
    export type State = BaseAutocomplete.Positioner.State;
  }
  export namespace Popup {
    export type Props = BaseAutocomplete.Popup.Props;
  }
  export namespace Arrow {
    export type Props = BaseAutocomplete.Arrow.Props;
  }
  export namespace Status {
    export type Props = BaseAutocomplete.Status.Props;
  }
  export namespace Empty {
    export type Props = BaseAutocomplete.Empty.Props;
  }
  export namespace Collection {
    export type Props = React.ComponentProps<
      typeof BaseAutocomplete.Collection
    >;
  }
  export namespace Row {
    export type Props = BaseAutocomplete.Row.Props;
  }
  export namespace Item {
    export type Props = BaseAutocomplete.Item.Props;
    export type State = BaseAutocomplete.Item.State;
  }
  export namespace Group {
    export type Props = BaseAutocomplete.Group.Props;
  }
  export namespace GroupLabel {
    export type Props = BaseAutocomplete.GroupLabel.Props;
  }
  export namespace Separator {
    export type Props = BaseAutocomplete.Separator.Props;
  }
}
 
export default Autocomplete;

Use Cases

Uncontrolled

For simple use cases, use the defaultValue prop to set an initial value without managing state. The component handles its own internal state. Pass items to the Root and use a render function inside List to display each item.

Controlled

For full control over the input value, use value and onValueChange props. This allows you to react to changes, validate input, or sync with external state.

Current value: (empty)

With clear button

Add Autocomplete.Clear to allow users to quickly reset the input. You can wrap the input and icons in a container to position them together.

Empty state

Use Autocomplete.Empty to show a message when no results match the search query. This provides feedback to users when their search doesn't match any options.