Skip to content

Instantly share code, notes, and snippets.

@wodin
Created August 15, 2025 21:57
Show Gist options
  • Save wodin/410deb2f85cdf5c90d6c783fc5ab9324 to your computer and use it in GitHub Desktop.
Save wodin/410deb2f85cdf5c90d6c783fc5ab9324 to your computer and use it in GitHub Desktop.
Emoji Picker
// https://stackblitz.com/edit/react-aria-emoji-picker?file=src%2FEmojiPicker.tsx
import {
Autocomplete,
Button,
GridLayout,
ListBox,
ListBoxItem,
Select,
SelectValue,
Size,
useFilter,
Virtualizer,
} from 'react-aria-components';
import { Popover } from './components/Popover';
import { SearchField } from './components/SearchField';
import _emojis from 'emojibase-data/en/compact.json';
import { Smile } from 'lucide-react';
import './EmojiPicker.css';
const emojis = _emojis.filter((e) => !e.label.startsWith('regional indicator'));
export function EmojiPicker() {
let { contains } = useFilter({ sensitivity: 'base' });
return (
<Select aria-label="Emoji" className="emoji-picker">
<Button>
<SelectValue>
{({ isPlaceholder, defaultChildren }) =>
isPlaceholder ? (
<Smile size={22} style={{ display: 'block' }} />
) : (
defaultChildren
)
}
</SelectValue>
</Button>
<Popover>
<Autocomplete filter={contains}>
<div className="emoji-picker-popover">
<SearchField aria-label="Search" autoFocus />
<Virtualizer
layout={GridLayout}
layoutOptions={{
minItemSize: new Size(32, 32),
maxItemSize: new Size(32, 32),
minSpace: new Size(4, 4),
preserveAspectRatio: true,
}}
>
<ListBox items={emojis} aria-label="Emoji list" layout="grid">
{(item) => <EmojiItem id={item.unicode} item={item} />}
</ListBox>
</Virtualizer>
</div>
</Autocomplete>
</Popover>
</Select>
);
}
function EmojiItem({ id, item }: { id: string; item: (typeof emojis)[0] }) {
return (
<ListBoxItem
id={id}
value={item}
textValue={item.label + (item.tags || []).join(' ')}
>
{item.unicode}
</ListBoxItem>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment