Components

Emoji Picker

A searchable, category-tabbed emoji picker with skin-tone selection and recently-used memory, built on the emoji-mart dataset.

Client Only

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

pnpm dlx @docyrus/cli add @docyrus/ui-emoji-picker
Required Packages(1 package)
pnpm add @emoji-mart/data
UI Primitives(4 components)
npx shadcn@latest add button input popover scroll-area

Usage

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 (
    <EmojiPicker side="top" align="start" onSelect={insert}>
      <Button variant="outline" size="icon" aria-label="Add emoji">
        <Smile className="size-4" />
      </Button>
    </EmojiPicker>
  );
}

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:

import { EmojiPickerPanel } from "@docyrus/ui/components/emoji-picker";

<EmojiPickerPanel onSelect={(emoji) => insert(emoji.native)} perLine={9} />

Features

FeatureNotes
SearchMatches emoji id, name, keywords, emoticons, and aliases (AND across terms).
Category tabsOnly the active category renders, so the popover stays light even across ~1,870 emoji.
Skin toneSix-tone selector; the choice persists to localStorage (docyrus:emoji-skin-tone).
Recently usedMost-recent-first strip, persisted to localStorage (docyrus:emoji-recents).
Native outputonSelect returns unicode (native) plus the :shortcode: โ€” no sprite sheet needed.

API Reference

EmojiPicker

PropTypeDefault
childrenReactNodeโ€” (the trigger, asChild)
onSelect(emoji: SelectedEmoji) => voidโ€”
defaultSkinTonenumber0
perLinenumber8
closeOnSelectbooleantrue
align'start' | 'center' | 'end''start'
side'top' | 'right' | 'bottom' | 'left''top'
openbooleanโ€” (controlled)
onOpenChange(open: boolean) => voidโ€”
contentClassNamestringโ€”

EmojiPickerPanel

PropTypeDefault
onSelect(emoji: SelectedEmoji) => voidโ€”
defaultSkinTonenumber0
perLinenumber8
classNamestringโ€”

Type Exports

TypeDescription
EmojiPickerPropsProps for EmojiPicker
EmojiPickerPanelPropsProps for EmojiPickerPanel
SelectedEmoji{ id, native, shortcode } โ€” the value passed to onSelect

SelectedEmoji

FieldTypeDescription
idstringemoji-mart id, e.g. "smile"
nativestringrendered unicode character (respects skin tone)
shortcodestringshortcode form, e.g. ":smile:"

Helpers

The component also re-exports a few pure helpers from the emoji dataset:

ExportSignatureDescription
searchEmojis(query: string, limit?: number) => string[]Emoji ids matching a query
resolveNative(id: string, skinTone: number) => stringUnicode char for an id + tone
getShortcode(id: string) => string":<id>:"

On this page