Emoji Picker
Preview: Popover
Emoji grid with search and category headings; arrow keys move through the grid (GridList), Enter picks. `autoFocus` focuses the search (on in the popover picker).
Import
import { EmojiGrid, EmojiPicker } from '@marbleui/ui'Props
<EmojiGrid>
| Prop | Type | Default | Description |
|---|---|---|---|
| onSelect *required | (emoji: string) => void | — | |
| autoFocus | boolean | — | |
| className | string | — | |
| emojis | [string, string, string][] | defaultEmojis |
<EmojiPicker>
Button that opens the emoji grid in a popover.
| Prop | Type | Default | Description |
|---|---|---|---|
| onSelect *required | (emoji: string) => void | — | |
| children | ReactNode | — | |
| label | string | 'Add emoji' |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Button · React Aria Collection · React Aria Dialog · React Aria DialogTrigger · React Aria GridList · React Aria GridListHeader · React Aria GridListItem · React Aria GridListSection · React Aria Input · React Aria SearchField. Follow the primitive's docs for the full keyboard map.
Usage notes
Emoji choice for reactions and message composers. EmojiPicker is a button that opens EmojiGrid in a popover; render EmojiGrid directly to embed it in your own panel.
<EmojiPicker label="Add reaction" onSelect={(emoji) => addReaction(messageId, emoji)} />
<EmojiGrid emojis={workspaceEmojis} onSelect={insertEmoji} />
- The built-in set is small (62 emoji, no skin tones or recents). Pass your own
emojisas[emoji, name, category]tuples with lowercase names; search matches the name, case-insensitively. Emoji are grouped under category headings in the order categories first appear. - In
EmojiPickerthe search box takes focus when the popover opens; an inlineEmojiGridonly does withautoFocus. Tab moves into the grid, arrow keys move between emoji, Enter picks; each emoji is announced by its name. EmojiPickercloses after a pick.childrenreplaces the default 🙂 trigger, andlabel(default “Add emoji”) is the trigger’s accessible name.
Example source · show· hide
import { useState } from 'react'
import { EmojiGrid, EmojiPicker } from './emoji-picker'
function InPopover() {
const [text, setText] = useState('Ship it ')
return <div className="flex w-80 items-center gap-1 rounded-lg border border-border bg-surface px-2"><span className="flex-1 truncate py-2 text-sm text-fg">{text}</span><EmojiPicker onSelect={(e) => setText((t) => t + e)} /></div>
}
function Inline() {
const [last, setLast] = useState('')
return <div className="flex flex-col gap-2 rounded-xl border border-border bg-surface p-3"><EmojiGrid onSelect={setLast} /><span aria-live="polite" className="text-sm text-fg-muted">Picked: {last || '—'}</span></div>
}
export const styles = { Popover: InPopover, Inline }Source: packages/ui/src/emoji-picker/emoji-picker.tsx
Catalog
- Category
- inputs
- Priority
- Extra
- Primitive
- composed
- React Aria
- GridList + SearchField