prebuiltui

Autocomplete

Component

About

Here is Autocomplete component

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Autocomplete to your project and fits it to your theme.

Code

How Autocomplete is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import * as React from 'react';
import { Autocomplete } from '@base-ui-components/react/autocomplete';

export default function ExampleEmojiPicker() {
  const [pickerOpen, setPickerOpen] = React.useState(false);
  const [textValue, setTextValue] = React.useState('');
  const [searchValue, setSearchValue] = React.useState('');

  const textInputRef = React.useRef<HTMLInputElement | null>(null);

  function handleInsertEmoji(value: string | null) {
    if (!value || !textInputRef.current) {
      return;
    }

    const emoji = value;
    const start = textInputRef.current.selectionStart ?? textInputRef.current.value.length ?? 0;
    const end = textInputRef.current.selectionEnd ?? textInputRef.current.value.length ?? 0;

    setTextValue((prev) => prev.slice(0, start) + emoji + prev.slice(end));
    setPickerOpen(false);

    const input = textInputRef.current;
    if (input) {
      input.focus();
      const caretPos = start + emoji.length;
      input.setSelectionRange(caretPos, caretPos);
    }
  }

  return (
    <div className="mx-auto w-[16rem]">
      <div className="flex items-center gap-2">
        <input
          ref={textInputRef}
          type="text"
          className="h-10 flex-1 font-normal rounded-md border border-gray-200 pl-3.5 text-base text-gray-900 focus:outline focus:outline-2 focus:-outline-offset-1 focus:outline-blue-800"
          placeholder="iMessage"
          value={textValue}
          onChange={(event) => setTextValue(event.target.value)}
        />

        <Autocomplete.Root
          items={emojiGroups}
          cols={COLUMNS}
          open={pickerOpen}
          onOpenChange={setPickerOpen}
          onOpenChangeComplete={() => setSearchValue('')}
          value={searchValue}
          onValueChange={(value, details) => {
            if (details.reason !== 'item-press') {
              setSearchValue(value);
            }
          }}
        >
          <Autocomplete.Trigger
            className="size-10 rounded-md border border-gray-200 bg-[canvas] text-[1.25rem] text-gray-900 outline-none hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-blue-800 data-[popup-open]:bg-gray-100"
            aria-label="Choose emoji"
          >
            ๐Ÿ˜€
          </Autocomplete.Trigger>
          <Autocomplete.Portal>
            <Autocomplete.Positioner className="outline-none" sideOffset={4} align="end">
              <Autocomplete.Popup className="[--input-container-height:3rem] max-w-[var(--available-width)] max-h-[min(20.5rem,var(--available-height))] origin-[var(--transform-origin)] rounded-lg bg-[canvas] shadow-lg shadow-gray-200 text-gray-900 outline-1 outline-gray-200 transition-[transform,scale,opacity] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[starting-style]:scale-90 data-[starting-style]:opacity-0 dark:shadow-none dark:-outline-offset-1 dark:outline-gray-300">
                <div className="mx-1 flex h-[var(--input-container-height)] w-64 items-center justify-center bg-[canvas] text-center">
                  <Autocomplete.Input
                    placeholder="Search emojisโ€ฆ"
                    className="h-10 w-[16rem] md:w-[20rem] font-normal rounded-md border border-gray-200 pl-3.5 text-base text-gray-900 focus:outline focus:outline-2 focus:-outline-offset-1 focus:outline-blue-800"
                  />
                </div>
                <Autocomplete.Empty className="px-4 pb-4 pt-2 text-[0.925rem] leading-4 text-gray-600 empty:m-0 empty:p-0">
                  No emojis found
                </Autocomplete.Empty>
                <Autocomplete.List className="max-h-[min(calc(20.5rem-var(--input-container-height)),calc(var(--available-height)-var(--input-container-height)))] overflow-auto scroll-pt-10 scroll-pb-[0.35rem] overscroll-contain">
                  {(group: EmojiGroup) => (
                    <Autocomplete.Group key={group.value} items={group.items} className="block">
                      <Autocomplete.GroupLabel className="sticky top-0 z-[1] m-0 w-full border-b border-gray-100 bg-[canvas] px-4 pb-1 pt-2 text-[0.75rem] font-semibold uppercase tracking-wide text-gray-600">
                        {group.label}
                      </Autocomplete.GroupLabel>
                      <div className="p-1" role="presentation">
                        {chunkArray(group.items, COLUMNS).map((row, rowIdx) => (
                          <Autocomplete.Row key={rowIdx} className="grid grid-cols-5">
                            {row.map((rowItem) => (
                              <Autocomplete.Item
                                key={rowItem.emoji}
                                value={rowItem}
                                className="group min-w-[var(--anchor-width)] select-none flex h-10 flex-col items-center justify-center rounded-md bg-transparent px-0.5 py-2 text-gray-900 outline-none data-[highlighted]:relative data-[highlighted]:z-0 data-[highlighted]:text-gray-50 data-[highlighted]:before:absolute data-[highlighted]:before:inset-0 data-[highlighted]:before:z-[-1] data-[highlighted]:before:rounded-md data-[highlighted]:before:bg-gray-200"
                                onClick={() => {
                                  handleInsertEmoji(rowItem.emoji);
                                  setPickerOpen(false);
                                }}
                              >
                                <span className="mb-1 text-[1.5rem] leading-none">
                                  {rowItem.emoji}
                                </span>
                              </Autocomplete.Item>
                            ))}
                          </Autocomplete.Row>
                        ))}
                      </div>
                    </Autocomplete.Group>
                  )}
                </Autocomplete.List>
              </Autocomplete.Popup>
            </Autocomplete.Positioner>
          </Autocomplete.Portal>
        </Autocomplete.Root>
      </div>
    </div>
  );
}

const COLUMNS = 5;

function chunkArray<T>(array: T[], size: number): T[][] {
  const result: T[][] = [];
  for (let i = 0; i < array.length; i += size) {
    result.push(array.slice(i, i + size));
  }
  return result;
}

interface EmojiItem {
  emoji: string;
  value: string;
  name: string;
}

interface EmojiGroup {
  value: string;
  label: string;
  items: EmojiItem[];
}

export const emojiCategories = [
  {
    label: 'Smileys & Emotion',
    emojis: [
      { emoji: '๐Ÿ˜€', name: 'grinning face' },
      { emoji: '๐Ÿ˜ƒ', name: 'grinning face with big eyes' },
      { emoji: '๐Ÿ˜„', name: 'grinning face with smiling eyes' },
      { emoji: '๐Ÿ˜', name: 'beaming face with smiling eyes' },
      { emoji: '๐Ÿ˜†', name: 'grinning squinting face' },
      { emoji: '๐Ÿ˜…', name: 'grinning face with sweat' },
      { emoji: '๐Ÿคฃ', name: 'rolling on the floor laughing' },
      { emoji: '๐Ÿ˜‚', name: 'face with tears of joy' },
      { emoji: '๐Ÿ™‚', name: 'slightly smiling face' },
      { emoji: '๐Ÿ™ƒ', name: 'upside-down face' },
      { emoji: '๐Ÿ˜‰', name: 'winking face' },
      { emoji: '๐Ÿ˜Š', name: 'smiling face with smiling eyes' },
      { emoji: '๐Ÿ˜‡', name: 'smiling face with halo' },
      { emoji: '๐Ÿฅฐ', name: 'smiling face with hearts' },
      { emoji: '๐Ÿ˜', name: 'smiling face with heart-eyes' },
      { emoji: '๐Ÿคฉ', name: 'star-struck' },
      { emoji: '๐Ÿ˜˜', name: 'face blowing a kiss' },
      { emoji: '๐Ÿ˜—', name: 'kissing face' },
      { emoji: 'โ˜บ๏ธ', name: 'smiling face' },
      { emoji: '๐Ÿ˜š', name: 'kissing face with closed eyes' },
      { emoji: '๐Ÿ˜™', name: 'kissing face with smiling eyes' },
      { emoji: '๐Ÿฅฒ', name: 'smiling face with tear' },
      { emoji: '๐Ÿ˜‹', name: 'face savoring food' },
      { emoji: '๐Ÿ˜›', name: 'face with tongue' },
      { emoji: '๐Ÿ˜œ', name: 'winking face with tongue' },
      { emoji: '๐Ÿคช', name: 'zany face' },
      { emoji: '๐Ÿ˜', name: 'squinting face with tongue' },
      { emoji: '๐Ÿค‘', name: 'money-mouth face' },
      { emoji: '๐Ÿค—', name: 'hugging face' },
      { emoji: '๐Ÿคญ', name: 'face with hand over mouth' },
    ],
  },
  {
    label: 'Animals & Nature',
    emojis: [
      { emoji: '๐Ÿถ', name: 'dog face' },
      { emoji: '๐Ÿฑ', name: 'cat face' },
      { emoji: '๐Ÿญ', name: 'mouse face' },
      { emoji: '๐Ÿน', name: 'hamster' },
      { emoji: '๐Ÿฐ', name: 'rabbit face' },
      { emoji: '๐ŸฆŠ', name: 'fox' },
      { emoji: '๐Ÿป', name: 'bear' },
      { emoji: '๐Ÿผ', name: 'panda' },
      { emoji: '๐Ÿจ', name: 'koala' },
      { emoji: '๐Ÿฏ', name: 'tiger face' },
      { emoji: '๐Ÿฆ', name: 'lion' },
      { emoji: '๐Ÿฎ', name: 'cow face' },
      { emoji: '๐Ÿท', name: 'pig face' },
      { emoji: '๐Ÿฝ', name: 'pig nose' },
      { emoji: '๐Ÿธ', name: 'frog' },
      { emoji: '๐Ÿต', name: 'monkey face' },
      { emoji: '๐Ÿ™ˆ', name: 'see-no-evil monkey' },
      { emoji: '๐Ÿ™‰', name: 'hear-no-evil monkey' },
      { emoji: '๐Ÿ™Š', name: 'speak-no-evil monkey' },
      { emoji: '๐Ÿ’', name: 'monkey' },
      { emoji: '๐Ÿ”', name: 'chicken' },
      { emoji: '๐Ÿง', name: 'penguin' },
      { emoji: '๐Ÿฆ', name: 'bird' },
      { emoji: '๐Ÿค', name: 'baby chick' },
      { emoji: '๐Ÿฃ', name: 'hatching chick' },
      { emoji: '๐Ÿฅ', name: 'front-facing baby chick' },
      { emoji: '๐Ÿฆ†', name: 'duck' },
      { emoji: '๐Ÿฆ…', name: 'eagle' },
      { emoji: '๐Ÿฆ‰', name: 'owl' },
      { emoji: '๐Ÿฆ‡', name: 'bat' },
    ],
  },
  {
    label: 'Food & Drink',
    emojis: [
      { emoji: '๐ŸŽ', name: 'red apple' },
      { emoji: '๐Ÿ', name: 'green apple' },
      { emoji: '๐ŸŠ', name: 'tangerine' },
      { emoji: '๐Ÿ‹', name: 'lemon' },
      { emoji: '๐ŸŒ', name: 'banana' },
      { emoji: '๐Ÿ‰', name: 'watermelon' },
      { emoji: '๐Ÿ‡', name: 'grapes' },
      { emoji: '๐Ÿ“', name: 'strawberry' },
      { emoji: '๐Ÿซ', name: 'blueberries' },
      { emoji: '๐Ÿˆ', name: 'melon' },
      { emoji: '๐Ÿ’', name: 'cherries' },
      { emoji: '๐Ÿ‘', name: 'peach' },
      { emoji: '๐Ÿฅญ', name: 'mango' },
      { emoji: '๐Ÿ', name: 'pineapple' },
      { emoji: '๐Ÿฅฅ', name: 'coconut' },
      { emoji: '๐Ÿฅ', name: 'kiwi fruit' },
      { emoji: '๐Ÿ…', name: 'tomato' },
      { emoji: '๐Ÿ†', name: 'eggplant' },
      { emoji: '๐Ÿฅ‘', name: 'avocado' },
      { emoji: '๐Ÿฅฆ', name: 'broccoli' },
      { emoji: '๐Ÿฅฌ', name: 'leafy greens' },
      { emoji: '๐Ÿฅ’', name: 'cucumber' },
      { emoji: '๐ŸŒถ๏ธ', name: 'hot pepper' },
      { emoji: '๐Ÿซ‘', name: 'bell pepper' },
      { emoji: '๐ŸŒฝ', name: 'ear of corn' },
      { emoji: '๐Ÿฅ•', name: 'carrot' },
      { emoji: '๐Ÿซ’', name: 'olive' },
      { emoji: '๐Ÿง„', name: 'garlic' },
      { emoji: '๐Ÿง…', name: 'onion' },
      { emoji: '๐Ÿฅ”', name: 'potato' },
    ],
  },
];

const emojiGroups: EmojiGroup[] = emojiCategories.map((category) => ({
  value: category.label,
  label: category.label,
  items: category.emojis.map((emoji) => ({
    ...emoji,
    value: emoji.name.toLowerCase(),
  })),
}));

Dependencies

More by prebuiltui

View all
  • Image Gallery

    Image Gallery

    1.3K
  • Hero Section

    Hero Section

    412
  • Feature Sections

    Feature Sections

    344
  • Testimonial

    Testimonial

    303
  • Cards

    Cards

    231
  • FAQ Sections

    FAQ Sections

    177
  • Form

    Form

    136
  • Login Form

    Login Form

    125