Emoji Picker

Pro

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.

Frequently used
Smileys & Emotion
People & Body
Animals & Nature
Pick an emoji

Last picked: 👋

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add emoji-picker

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Anatomy#

import {  EmojiPicker,  EmojiPickerPopover,  EmojiPickerTrigger,  EmojiPickerContent,} from "@/components/ui/emoji-picker"<EmojiPicker /><EmojiPickerPopover>  <EmojiPickerTrigger />  <EmojiPickerContent /></EmojiPickerPopover>

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.

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Density#

Density retunes the panel width, viewport height, grid gap, and glyph size, never color or radius. compact is the default, sized for application UI; comfortable opens it up for marketing pages. Without a prop it resolves from the nearest DensityProvider.

comfortable
Frequently used
Smileys & Emotion
People & Body
Animals & Nature
Pick an emoji
compact
Frequently used
Smileys & Emotion
People & Body
Animals & Nature
Pick an emoji

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.

Smileys & Emotion
People & Body
Animals & Nature

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.

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

API reference#

EmojiPicker

The whole picker: search, category nav, grid and preview footer. Forwards all native <div> props except onSelect; every part accepts className, merged last.

PropTypeDefaultDescription
onEmojiSelect(emoji: string, datum: EmojiDatum) => void-Called with the glyph and its { char, name, keywords } record when an emoji is chosen.
dataEmojiCategory[]emojiCategoriesThe categorized dataset; swap in a full Unicode export without touching the component.
presetsstring[]defaultEmojiPresetsThe fixed frequently-used row pinned above the categories.
recentstring[]-Controlled recently-used list, most recent first.
defaultRecentstring[][]Initial recents when uncontrolled; replaced by the persisted list once it loads.
onRecentChange(recent: string[]) => void-Called whenever the recents change, controlled or not.
recentLimitnumber24Most recents kept.
storageKeystring | null"koala-emoji-recent"localStorage key that persists uncontrolled recents; null turns persistence off.
showSearchbooleantrueRender the search row.
showPreviewbooleantrueRender the footer that previews the hovered or focused emoji.
showRecentbooleantrueRender and track the recently-used row; off, picks are not recorded.
showPresetsbooleantrueRender the presets row.
categoriesPerPagenumber3Categories mounted at first and per scroll batch; search results stream in batches of this times 16.
searchPlaceholderstring"Search emoji"Placeholder of the search field.
recentLabelstring"Recently used"Header and nav label of the recents row.
presetsLabelstring"Frequently used"Header and nav label of the presets row.
previewPlaceholderstring"Pick an emoji"Footer hint before anything is hovered or focused.
density"comfortable" | "compact""compact"Width (352px or 320px), viewport height, grid gap and glyph size. Falls back to the nearest DensityProvider.
Data attributeDescription
data-activetrue on the category nav button whose section is at the top of the viewport.
CSS variableDescription
--surfaceSet to var(--popover), so the nested search Input blends with the panel.

EmojiPickerPopover

The popover root, Radix Popover.Root; renders no DOM and lets the content close itself on select.

PropTypeDefaultDescription
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void-Called when the popover opens or closes, including the close after a pick.
modalbooleanfalseBlock outside interaction and trap focus while open.

EmojiPickerTrigger

The button that opens the picker, Radix Popover.Trigger. Renders a <button> and forwards all its props.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto your own button instead of rendering one.
Data attributeDescription
data-state"open" or "closed".

EmojiPickerContent

Radix Popover.Content portalled to the body, holding an EmojiPicker. Radix Content props (placement, collisions, focus and dismiss callbacks) go to the popover; every other prop, EmojiPicker options and native div props alike, goes to the picker. className styles the popover wrapper; pickerClassName styles the picker.

PropTypeDefaultDescription
closeOnSelectbooleantrueClose the popover after a pick.
pickerClassNamestring-Classes for the inner picker surface.
side"top" | "right" | "bottom" | "left""bottom"Preferred side of the trigger; flips on collision.
sideOffsetnumber8Gap in pixels between the trigger and the picker.
align"start" | "center" | "end""start"Alignment against the trigger.
alignOffsetnumber0Offset in pixels from the align edge.
avoidCollisionsbooleantrueFlip and shift to stay inside the viewport.
collisionPaddingnumber | Partial<Record<Side, number>>0Distance from the boundary edges that counts as a collision.
collisionBoundaryElement | Element[] | null[]Extra elements used as the collision boundary.
sticky"partial" | "always""partial"Keep the picker in view while the trigger scrolls.
hideWhenDetachedbooleanfalseHide the picker when the trigger is fully clipped.
onOpenAutoFocus(event: Event) => void-Fires when focus moves into the picker on open; preventDefault() to keep it on the trigger.
onCloseAutoFocus(event: Event) => void-Fires when focus returns to the trigger on close.
onEscapeKeyDown(event: KeyboardEvent) => void-Fires on Escape; preventDefault() keeps it open.
onInteractOutside(event: Event) => void-Fires on a pointer or focus outside; preventDefault() keeps it open. onPointerDownOutside and onFocusOutside are also accepted.
forceMounttrue-Keep the popover mounted while closed, for external animation control.
Data attributeDescription
data-state"open" or "closed".
data-sideThe side the popover actually opened on, after collisions; drives the slide direction.
data-align"start", "center" or "end".

FAQ#

Render the bare `EmojiPicker` when the panel lives permanently in the layout, for example a sidebar or a reaction tray. Use `EmojiPickerPopover` with `EmojiPickerTrigger` and `EmojiPickerContent` for the common click-a-button-then-pick flow, since the content closes the popover on select by default.