A fast, lightweight, zero-dependency emoji picker for React.
- Virtual scrolling — renders only visible rows for buttery-smooth performance
- Skin tone support — per-emoji skin tone modifiers
- Search — instant filtering across names and keywords
- Recent emojis — localStorage-backed recently-used list
- Full accessibility — ARIA roles, keyboard navigation, focus management
- Themeable — light / dark / auto via CSS custom properties
- Tiny — zero runtime dependencies, tree-shakeable ESM + CJS
- TypeScript — complete type definitions included
# npm
npm install @alihesari/emoji-picker
# pnpm
pnpm add @alihesari/emoji-picker
# yarn
yarn add @alihesari/emoji-pickerPeer dependencies:
react >=18andreact-dom >=18.
import { EmojiPicker } from "@alihesari/emoji-picker";
import "@alihesari/emoji-picker/styles.css";
function App() {
return (
<EmojiPicker
onEmojiClick={(emoji) => {
console.log(emoji.emoji); // 😎
console.log(emoji.name); // "smiling face with sunglasses"
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
onEmojiClick |
(emoji: EmojiData) => void |
required | Called when an emoji is clicked |
width |
number |
350 |
Width in pixels |
height |
number |
450 |
Height in pixels |
theme |
"light" | "dark" | "auto" |
"auto" |
Color theme (auto follows system) |
categories |
CategoryId[] |
all categories | Which categories to display |
searchPlaceholder |
string |
"Search emojis…" |
Placeholder for the search input |
searchDisabled |
boolean |
false |
Hide the search bar |
skinTonesDisabled |
boolean |
false |
Hide the skin tone selector |
defaultSkinTone |
SkinTone |
"neutral" |
Default skin tone |
recentEnabled |
boolean |
true |
Enable the Recent category |
maxRecent |
number |
30 |
Max number of recent emojis to store |
autoFocusSearch |
boolean |
false |
Auto-focus search on mount |
previewConfig |
PreviewConfig |
{ enabled: true, showName: true } |
Configure the preview bar |
columns |
number |
8 |
Number of columns in the grid |
emojiSize |
number |
40 |
Size of each emoji button (px) |
className |
string |
— | Extra CSS class on root |
style |
CSSProperties |
— | Inline styles on root |
interface EmojiData {
emoji: string; // The rendered emoji (with skin tone applied)
name: string; // Human-readable name
originalEmoji: string; // Emoji without skin tone
skinTone: SkinTone; // Which skin tone was applied
}
type SkinTone = "neutral" | "light" | "medium-light" | "medium" | "medium-dark" | "dark";
type CategoryId =
| "recent" | "smileys" | "people" | "animals" | "food"
| "travel" | "activities" | "objects" | "symbols" | "flags";All colours use CSS custom properties. Override them on .ep-root or a parent:
.my-picker {
--ep-bg: #1a1a2e;
--ep-border: #16213e;
--ep-text: #eaeaea;
--ep-text-secondary: #888;
--ep-hover-bg: #16213e;
--ep-active-bg: #0f3460;
--ep-input-bg: #0a0a1a;
--ep-input-border: #16213e;
--ep-radius: 12px;
}<EmojiPicker className="my-picker" onEmojiClick={handleClick} />| Property | Description |
|---|---|
--ep-bg |
Background colour |
--ep-border |
Border & divider colour |
--ep-text |
Primary text colour |
--ep-text-secondary |
Secondary/muted text |
--ep-hover-bg |
Hover background |
--ep-active-bg |
Active/selected background |
--ep-input-bg |
Search input background |
--ep-input-border |
Search input border |
--ep-radius |
Root border radius |
--ep-font-family |
Font family |
- Escape — clears the search query
- Tab — navigates between search, categories, and the grid
- Standard focus management with
:focus-visibleoutlines
| Feature | @alihesari/emoji-picker | emoji-picker-react |
|---|---|---|
| Runtime dependencies | 0 | 5+ |
| Virtual scrolling | Built-in | No |
| Bundle size (gzipped) | ~25KB | ~90KB |
| CSS-in-JS | No (CSS custom props) | Yes |
| SSR safe | Yes | Partial |
| TypeScript | Full | Full |
| Tree-shakeable | Yes | Partial |
# Install dependencies
pnpm install
# Build
pnpm build
# Dev (watch mode)
pnpm dev
# Type check
pnpm typecheckMIT © Ali Hesari


