Skip to content

rudrodip/asyncr

Repository files navigation

Async Select Component

A modern, accessible, and customizable async select component for React applications. Built with TypeScript and shadcn/ui components.

Async Select

Installation

The Async Select Component is built through the composition of <Popover /> and the <Command /> components from shadcn/ui.

See installation instructions for the Popover and the Command components.

Basic Usage

import { AsyncSelect } from "@/components/async-select";

function MyComponent() {
  const [value, setValue] = useState("");

  return (
    <AsyncSelect<DataType>
      fetcher={fetchData}
      renderOption={(item) => <div>{item.name}</div>}
      getOptionValue={(item) => item.id}
      getDisplayValue={(item) => item.name}
      label="Select"
      value={value}
      onChange={setValue}
    />
  );
}

Props

Required Props

Prop Type Description
fetcher (query?: string) => Promise<T[]> Async function to fetch options
renderOption (option: T) => React.ReactNode Function to render each option in the dropdown
getOptionValue (option: T) => string Function to get unique value from option
getDisplayValue (option: T) => React.ReactNode Function to render selected value
value string Currently selected value
onChange (value: string) => void Callback when selection changes
label string Label for the select field

Optional Props

Prop Type Default Description
preload boolean false Enable preloading all options
filterFn (option: T, query: string) => boolean - Custom filter function for preload mode
notFound React.ReactNode - Custom not found message/component
loadingSkeleton React.ReactNode - Custom loading state component
placeholder string "Select..." Placeholder text
disabled boolean false Disable the select
width string | number "200px" Custom width
className string - Custom class for popover
triggerClassName string - Custom class for trigger button
noResultsMessage string - Custom no results message
clearable boolean true Allow clearing selection

Examples

Async Mode

<AsyncSelect<User>
  fetcher={searchUsers}
  renderOption={(user) => (
    <div className="flex items-center gap-2">
      <Image
        src={user.avatar}
        alt={user.name}
        width={24}
        height={24}
        className="rounded-full"
      />
      <div className="flex flex-col">
        <div className="font-medium">{user.name}</div>
        <div className="text-xs text-muted-foreground">{user.role}</div>
      </div>
    </div>
  )}
  getOptionValue={(user) => user.id}
  getDisplayValue={(user) => (
    <div className="flex items-center gap-2">
      <Image
        src={user.avatar}
        alt={user.name}
        width={24}
        height={24}
        className="rounded-full"
      />
      <div className="flex flex-col">
        <div className="font-medium">{user.name}</div>
        <div className="text-xs text-muted-foreground">{user.role}</div>
      </div>
    </div>
  )}
  notFound={<div className="py-6 text-center text-sm">No users found</div>}
  label="User"
  placeholder="Search users..."
  value={selectedUser}
  onChange={setSelectedUser}
  width="375px"
/>

Preload Mode

<AsyncSelect<User>
  fetcher={searchAllUsers}
  preload
  filterFn={(user, query) => user.name.toLowerCase().includes(query.toLowerCase())}
  renderOption={(user) => (
    <div className="flex items-center gap-2">
      <Image
        src={user.avatar}
        alt={user.name}
        width={24}
        height={24}
        className="rounded-full"
      />
      <div className="flex flex-col">
        <div className="font-medium">{user.name}</div>
        <div className="text-xs text-muted-foreground">{user.role}</div>
      </div>
    </div>
  )}
  getOptionValue={(user) => user.id}
  getDisplayValue={(user) => user.name}
  label="User"
  value={selectedUser}
  onChange={setSelectedUser}
/>

TypeScript Interface

interface AsyncSelectProps<T> {
  fetcher: (query?: string) => Promise<T[]>;
  preload?: boolean;
  filterFn?: (option: T, query: string) => boolean;
  renderOption: (option: T) => React.ReactNode;
  getOptionValue: (option: T) => string;
  getDisplayValue: (option: T) => React.ReactNode;
  notFound?: React.ReactNode;
  loadingSkeleton?: React.ReactNode;
  value: string;
  onChange: (value: string) => void;
  label: string;
  placeholder?: string;
  disabled?: boolean;
  width?: string | number;
  className?: string;
  triggerClassName?: string;
  noResultsMessage?: string;
  clearable?: boolean;
}

About

Async Select component built with React & ShadCN UI

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •