An emoji picker with autocomplete, virtualized scrolling, and arrow key navigation.
Example
EmojiPicker.css
Example
EmojiPicker.css
Example
EmojiPicker.css
import {Autocomplete, GridLayout, ListBox, ListBoxItem, Select, SelectValue, Size, useFilter, Virtualizer} from 'react-aria-components';
import {Button} from './Button';
import {Popover} from './Popover';
import {SearchField} from './SearchField';
import _emojis from 'emojibase-data/en/compact.json';
import './EmojiPicker.css';
type Emoji = (typeof _emojis)[0];
const emojis: Emoji[] = _emojis.filter((e) => typeof e.label === 'string' && !e.label.startsWith('regional indicator'));
export default function EmojiPicker() {
let {contains} = useFilter({ sensitivity: 'base' });
return (
<Select aria-label="Emoji" className="emoji-picker" defaultValue="🥳">
<Button variant="secondary">
<SelectValue />
</Button>
<Popover placement="bottom" className="emoji-picker-popover">
<Autocomplete filter={contains}>
<SearchField aria-label="Search" placeholder="Search emoji" autoFocus />
<Virtualizer
layout={GridLayout}
layoutOptions={{
minItemSize: new Size(32, 32),
maxItemSize: new Size(32, 32),
minSpace: new Size(4, 4),
preserveAspectRatio: true,
}}>
<ListBox className="emoji-list" items={emojis} aria-label="Emoji list" layout="grid">
{(item) => <EmojiItem id={String(item.unicode)} item={item} />}
</ListBox>
</Virtualizer>
</Autocomplete>
</Popover>
</Select>
);
}
function EmojiItem({ id, item }: { id: string; item: Emoji }) {
return (
<ListBoxItem
id={id}
value={item}
textValue={(item.label || '') + (Array.isArray(item.tags) ? item.tags.join(' ') : '')}
className="emoji-item">
{item.unicode}
</ListBoxItem>
);
}