Emoji Picker
A complete picker: searchable, with a scroll-spy category nav, a pinned frequently-used row, persisted recents and a hovered preview. Categories mount lazily, so a large set never paints at once.
Installation#
Usage#
import { EmojiPicker } from "@/components/ui/emoji-picker"
export function Example() {
return <EmojiPicker onEmojiSelect={(char, datum) => console.log(char, datum.name)} />
}In a popover#
The most common flow: a trigger opens the picker in a Radix popover that closes on select. Compose EmojiPickerPopover, EmojiPickerTrigger (use asChild to keep your own button), and EmojiPickerContent.
Recents & presets#
The picker tracks a recently used row automatically and persists it to localStorage (set storageKey, or pass null to disable). For full control, drive recent / onRecentChange yourself. The presets row is a fixed quick-pick list pinned above the categories; override it with the presets prop.
<EmojiPicker
presets={["👍", "❤️", "😂", "🎉", "🚀"]}
recent={recent}
onRecentChange={setRecent}
recentLimit={16}
/>Density#
Density retunes the panel width, viewport height, grid gap, and glyph size, never color or radius. comfortable is the marketing default; compact tightens it for application UI. It also resolves from a surrounding DensityProvider.
Minimal#
Every region is toggleable. Drop the preview footer and the recents/presets rows with showPreview, showRecent, and showPresets for a lean search-and-grid.
Performance#
Categories mount in batches. Only categoriesPerPage are in the DOM at first, and a bottom sentinel reveals the next batch as you scroll (search results stream the same way). Counts only ever grow, so scroll height never jitters. Lower the value for a lighter first paint, or pass your own fuller dataset via data without changing the component.
// Mount two categories per scroll batch instead of three.
<EmojiPicker categoriesPerPage={2} data={myFullUnicodeSet} />