# Emoji Picker URL: /docs/web/components/emoji-picker A searchable, category-tabbed emoji picker with skin-tone selection and recently-used memory, built on the emoji-mart dataset. **Demo:** ```tsx 'use client'; // @custom-demo import { useRef, useState } from 'react'; import { Smile } from 'lucide-react'; import { EmojiPicker, type SelectedEmoji } from '@docyrus/ui/components/emoji-picker'; import { Button } from '@docyrus/ui/primitives/ui/button'; export function EmojiPickerDemo() { const [text, setText] = useState('Ship it '); const ref = useRef ); } ``` The `EmojiPicker` wraps a trigger of your choice in a popover. It is fully self-contained — search, category tabs, a skin-tone selector, and a recently-used strip (persisted to `localStorage`) — and returns the chosen emoji as native unicode so it drops straight into any text input, `contentEditable`, or message body. It powers the comment composer, email composer, and instant-message composer. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-emoji-picker ``` **Dependencies:** - [lucide-react](https://www.npmjs.com/package/lucide-react) - [@emoji-mart/data](https://www.npmjs.com/package/@emoji-mart/data) ## Usage ```tsx import { EmojiPicker, type SelectedEmoji } from "@docyrus/ui/components/emoji-picker"; import { Button } from "@docyrus/ui/primitives/ui/button"; import { Smile } from "lucide-react"; function Composer() { const insert = (emoji: SelectedEmoji) => { console.log(emoji.native); // "😄" console.log(emoji.shortcode); // ":smile:" }; return ( ); } ``` The single child is the trigger (rendered via Radix `asChild`, so pass one focusable element). `onSelect` fires with the emoji already resolved for the active skin tone. ### Bare panel Use `EmojiPickerPanel` to embed the picker without the popover — e.g. inside your own dropdown or a fixed side panel: ```tsx import { EmojiPickerPanel } from "@docyrus/ui/components/emoji-picker"; insert(emoji.native)} perLine={9} /> ``` ## Features | Feature | Notes | |---------|-------| | Search | Matches emoji id, name, keywords, emoticons, and aliases (AND across terms). | | Category tabs | Only the active category renders, so the popover stays light even across ~1,870 emoji. | | Skin tone | Six-tone selector; the choice persists to `localStorage` (`docyrus:emoji-skin-tone`). | | Recently used | Most-recent-first strip, persisted to `localStorage` (`docyrus:emoji-recents`). | | Native output | `onSelect` returns unicode (`native`) plus the `:shortcode:` — no sprite sheet needed. | ## API Reference ### EmojiPicker | Prop | Type | Default | |------|------|---------| | `children` | `ReactNode` | — (the trigger, `asChild`) | | `onSelect` | `(emoji: SelectedEmoji) => void` | — | | `defaultSkinTone` | `number` | `0` | | `perLine` | `number` | `8` | | `closeOnSelect` | `boolean` | `true` | | `align` | `'start' \| 'center' \| 'end'` | `'start'` | | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | | `open` | `boolean` | — (controlled) | | `onOpenChange` | `(open: boolean) => void` | — | | `contentClassName` | `string` | — | ### EmojiPickerPanel | Prop | Type | Default | |------|------|---------| | `onSelect` | `(emoji: SelectedEmoji) => void` | — | | `defaultSkinTone` | `number` | `0` | | `perLine` | `number` | `8` | | `className` | `string` | — | ## Type Exports | Type | Description | |------|-------------| | `EmojiPickerProps` | Props for `EmojiPicker` | | `EmojiPickerPanelProps` | Props for `EmojiPickerPanel` | | `SelectedEmoji` | `{ id, native, shortcode }` — the value passed to `onSelect` | ### SelectedEmoji | Field | Type | Description | |-------|------|-------------| | `id` | `string` | emoji-mart id, e.g. `"smile"` | | `native` | `string` | rendered unicode character (respects skin tone) | | `shortcode` | `string` | shortcode form, e.g. `":smile:"` | ## Helpers The component also re-exports a few pure helpers from the emoji dataset: | Export | Signature | Description | |--------|-----------|-------------| | `searchEmojis` | `(query: string, limit?: number) => string[]` | Emoji ids matching a query | | `resolveNative` | `(id: string, skinTone: number) => string` | Unicode char for an id + tone | | `getShortcode` | `(id: string) => string` | `"::"` |