npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

emoji-picker-react

v5.1.4

Published

React emoji picker: batteries included, or BYOD (bring your own design, design language and design library). Shared keyboard navigation, localization and virtualization across styled, unstyled and composed UIs.

Readme

Emoji Picker React

Batteries included. Or BYOD — bring your own design.

One emoji-picker engine, with two ways to build your UI: use the complete picker, or bring your own design, design language and design library.

Batteries included: npm install emoji-picker-react (or npx shadcn@latest add the registry component), render <EmojiPicker />, done — a complete, accessible, themed picker with search, categories, skin tones, recents and keyboard navigation. No CSS import, no configuration, no design work.

BYOD (bring your own design): pass unstyled, or compose your own layout from emoji-picker-react/primitives, and style it with whatever your app already uses — Tailwind, shadcn/ui, CSS Modules, Emotion, styled-components, MUI or plain CSS. Accessibility, keyboard navigation and virtualization stay built in.

npm downloads

Live demo · Quick start · Props · Compare pickers · BYOD · Common tasks · For AI assistants · Report a bug · Sponsor

image

reactions

Quick start

npm install emoji-picker-react

Using shadcn/ui? Add the registry component instead. It installs the package and renders with your theme tokens (bg-popover, bg-accent, border-input), so light, dark and custom themes follow automatically:

npx shadcn@latest add https://ealush.com/emoji-picker-react/r/emoji-picker.json

Then import { EmojiPicker } from '@/components/ui/emoji-picker' and wrap it in your Popover (setup and focus behavior).

Requires React 16.8 or later and Node 18 or later in your build toolchain. (Contributors to this repository use Node 24; see CONTRIBUTING.md.)

import EmojiPicker from 'emoji-picker-react';

function App() {
  return (
    <EmojiPicker onEmojiClick={(emojiData) => console.log(emojiData.emoji)} />
  );
}

That is the whole setup. onEmojiClick receives an EmojiClickData object (emoji, unified, names, imageUrl, activeSkinTone, …) and the event.

<EmojiPicker colorScheme="dark" width={320} height={400} previewConfig={{ showPreview: false }} />

See PROPS.md for every prop.

Offline, CSP-safe, no CDN

  • No third-party requests. Emoji data for 28 languages ships in the package and native emoji are the default, so the picker never contacts a CDN. A dataset the primitives entry defers is a chunk your bundler serves from your own origin. Image styles (Apple, Google, Facebook, Twitter) load from jsDelivr unless getEmojiUrl points at your own host.
  • Strict Content Security Policy. Styles are injected once per document; pass your policy's nonce and nothing needs 'unsafe-inline'.
  • Coming from emoji-mart? No @emoji-mart/data to bundle by hand, React 19 support and a nonce: see Migrating from emoji-mart.
  • Tested as installed. Every pull request installs the packed tarball into fresh Vite, CommonJS and shadcn/ui apps, serves them under a strict CSP, and fails on any third-party request or CSP violation (playwright/packed-consumer.spec.ts).

Features

  • Batteries included — one component, sensible defaults, light/dark/auto themes, no stylesheet to import.
  • BYOD — unstyled drops the chrome and composable primitives let you build any layout, styled with plain CSS, CSS Modules, Tailwind, shadcn/ui, Emotion, styled-components or MUI. See 25 designs built this way.
  • Accessible — full keyboard navigation, ARIA grid semantics, localizable labels, right-to-left layouts, reduced-motion support and automated axe checks.
  • Reactions mode — a compact reactions bar that expands to the full picker.
  • Localized — 28 emoji datasets; translate search, categories, previews, reactions, skin tones, loading errors and retry controls, including accessible announcements.
  • Emoji styles — native (default), Apple, Google, Facebook, Twitter; native mode filters detected unsupported OS glyphs, including individual sequences and skin tones. See detection limits.
  • Custom emojis — image-based emojis, optionally in their own named groups.
  • Lean when you want — the primitives entry loads the dataset on demand (a 35 KiB min+gz startup budget including ShipStyles; see the consumer gate); a framework-free data API for search and lookup.
  • Modern React — React 16.8 through 19, SSR, React Server Components ("use client" entries), TypeScript types included; ships ES2020 for every browser since 2020 and any modern bundler.

Upgrading from v4? Most code works unchanged; the default emoji style is now native, and markup, styling and package boundaries have changed. See Migrating from v4. The APIs on this page need emoji-picker-react@5 or later.

Compare emoji pickers

v5 supports both a complete styled picker and unstyled React composition, with custom image emoji, recents, category navigation and reactions built in.

| Capability | emoji-picker-react v5 | Emoji Mart + React wrapper | Frimousse | emoji-picker-element | | --- | --- | --- | --- | --- | | Complete styled picker | Built in | Built in | Compose/style parts or use shadcn starter | Built in | | Unstyled React primitives | Built in | No React primitives API | Built in | Web component | | Keyboard navigation, search and skin tones | Built in | Built in | Built in | Built in | | Localized search and UI | Built in | Built in | Built in | Built in | | Native glyph support filtering | Built in | Built in | Built in for default data resolver | Built in | | Custom image emoji and named groups | Built in | Built in | Custom data and cell rendering | Built in | | Recent/frequent emoji | Built in | Built in | App-owned | Built-in favorites | | Category navigation controls | Built in | Built in | App-owned | Built in | | Compact reactions bar with expansion | Built in | App-owned | App-owned | App-owned | | Standalone emoji search API | Framework-free /data entry | SearchIndex | Metadata lookup only | Browser Database API | | Persistent dataset cache | Remote cache policy is app-owned | Remote cache policy is app-owned | Built-in localStorage cache | Built-in IndexedDB cache |

“App-owned” means the application implements the feature. Custom renderers and data resolvers can extend the supplied behavior. Bundled data in emoji-picker-react and Emoji Mart can be used offline without a remote cache.

Audited October 20, 2026 against Emoji Mart 5.6.0 / React wrapper 1.1.1, Frimousse 0.4.0 and emoji-picker-element 1.29.1. See the full comparison and sources for API details, v5 capabilities, native-detection limits and data-loading APIs.

Copyable integration prompts

Use the setup, customization, recipe adoption and v5 migration prompts to give a coding agent a concrete starting point for your application. Each prompt checks the installed version and keeps integration behavior explicit.

Choose your path

| You want | Use | You style | | --- | --- | --- | | A complete picker now | <EmojiPicker /> | Nothing | | The built-in look in your brand's colors and sizes | <EmojiPicker /> + colorScheme and --epr-* variables | A few variables | | Your own design on the supplied layout | <EmojiPicker unstyled /> | Every part, with your CSS or styling library | | Your own layout and components | emoji-picker-react/primitives | Layout and parts, or reuse the built-in look with appearance="default" |

Every path keeps the same behavior: keyboard navigation, focus management, ARIA semantics, virtualization, skin tones, variations, recents, search and localization.

One rule decides how styling works: color variables (--epr-bg-color, --epr-highlight-color, …) theme the built-in look. unstyled and a bare primitives Root remove the built-in look, so color variables have no effect there. You style the parts directly. Size variables (--epr-emoji-size, paddings, heights) apply in every mode.

BYOD: bring your own design

Bring your own design, design language and design library. Keep the supplied layout with unstyled, or compose the parts and use your own input, emoji buttons, category headers, preview and tone controls. Search, localization, keyboard navigation and virtualization share the same engine in both paths.

The picker needs no stylesheet import. Its CSS is injected automatically, once per document or shadow root, and scoped so it never leaks into your app.

How overrides work, so you never fight specificity:

  • Design variables (--epr-*) always yield to your CSS. Their defaults are emitted at zero specificity, so a single class wins in any load order.
  • Parts are targetable with stable [data-epr-part="…"] selectors: root, panel, search, search-input, search-clear, skin-tone, skin-tone-button, category-nav, category-tab, viewport, list, category, category-label, category-content, emoji, variation-picker, preview, empty, loading, load-error, reactions, reaction, expand-reactions. State is exposed as [data-epr-active] (hovered or focused emoji, active tab or tone), [data-epr-highlighted] (the keyboard highlight with keepSearchFocus), [aria-selected="true"] (current tab) and [aria-pressed="true"] (current tone). The full contract: docs/v5/STYLING.md.
  • CSS ownership is explicit. Library classes protect layout from ordinary element/universal resets. More specific rules and !important can still override geometry; preserve the structural contract.
  • Layered frameworks: with Tailwind v4 (or any @layer-based setup), pass cssLayer="epr" and declare the layer first — @layer epr, theme, base, components, utilities; — so utilities override the picker.

Theme the built-in look with CSS variables

.my-picker {
  --epr-bg-color: #0f172a;
  --epr-category-label-bg-color: #0f172ae6;
  --epr-search-input-bg-color: #1e293b;
  --epr-search-input-bg-color-active: #1e293b;
  --epr-picker-border-color: #1e293b;
  --epr-text-color: #cbd5e1;
  --epr-highlight-color: #22d3ee;
  --epr-hover-bg-color: #1e293b;
  --epr-focus-bg-color: #334155;
  --epr-emoji-size: 28px;
}

.my-picker [data-epr-part='category-label'] {
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
<EmojiPicker colorScheme="dark" className="my-picker" />

Sticky category labels and the search field have their own surfaces, so set their variables along with --epr-bg-color. All variables: CSS_VARIABLES.md.

Unstyled: your design, the supplied layout

unstyled removes decorative styling from every part: colors, borders, rounding, button resets, typography and the search icon. Layout, geometry, virtualization, keyboard behavior and visibility stay managed. You start from plain browser controls and style the parts with your own CSS. This is a complete starting point:

.my-picker {
  background: Canvas;
  color: CanvasText;
  border: 1px solid #8884;
  border-radius: 12px;
  font: 14px system-ui, sans-serif;
}
.my-picker [data-epr-part='search-input'] {
  padding-inline-start: 10px;
  border: 1px solid #8886;
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
}
.my-picker :is([data-epr-part='emoji'], [data-epr-part='category-tab'],
    [data-epr-part='search-clear'], [data-epr-part='skin-tone-button']) {
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.my-picker [data-epr-part='emoji'][data-epr-active],
.my-picker [data-epr-part='category-tab'][aria-selected='true'] {
  background: #8882;
}
.my-picker :is(button, input):focus-visible {
  outline: 2px solid Highlight;
  outline-offset: -2px;
}
.my-picker [data-epr-part='category-label'] {
  font-size: 12px;
  font-weight: 600;
  background: Canvas;
}
.my-picker [data-epr-part='variation-picker'] {
  background: Canvas;
  border: 1px solid #8884;
  border-radius: 10px;
  box-shadow: 0 4px 12px #0003;
}
<EmojiPicker unstyled className="my-picker" />

It follows the page's light or dark color-scheme. Keep a visible focus style on buttons and the input. Overlays such as the skin-tone variation popover (variation-picker) are transparent until you give them a surface. The same selectors work from CSS Modules (:global([data-epr-part='emoji']) inside your module class), Emotion, styled-components, Tailwind arbitrary variants ([&_[data-epr-part=emoji]]:rounded-lg) and MUI's styled.

Tailwind CSS (v4)

/* app.css */
@layer epr, theme, base, components, utilities;
@import 'tailwindcss';
<EmojiPicker
  cssLayer="epr"
  className="rounded-2xl shadow-xl [--epr-bg-color:var(--color-white)] [--epr-highlight-color:var(--color-indigo-600)] [--epr-hover-bg-color:var(--color-indigo-50)] [&_[data-epr-part=emoji]]:rounded-lg"
/>

Utilities apply to the root; arbitrary properties set variables and arbitrary variants reach the parts. For a fully custom design, add unstyled and style the parts with utilities instead of variables.

shadcn/ui

Map the picker's variables to shadcn's theme variables once; light, dark and custom themes then follow automatically:

.emoji-picker {
  --epr-bg-color: var(--popover);
  --epr-category-label-bg-color: var(--popover);
  --epr-text-color: var(--muted-foreground);
  --epr-highlight-color: var(--primary);
  --epr-hover-bg-color: var(--accent);
  --epr-focus-bg-color: var(--accent);
  --epr-picker-border-color: var(--border);
  --epr-search-input-bg-color: transparent;
  --epr-search-border-color: var(--input);
  --epr-search-border-color-active: var(--ring);
}
<PopoverContent className="w-auto p-0">
  <EmojiPicker cssLayer="epr" className="emoji-picker" onEmojiClick={(e) => insert(e.emoji)} />
</PopoverContent>

For a component that uses your shadcn theme classes directly, install the registry component.

Emotion, styled-components and MUI

import styled from '@emotion/styled'; // or 'styled-components', or '@mui/material/styles'

const ThemedPicker = styled(EmojiPicker)`
  --epr-bg-color: ${(p) => p.theme.colors.surface};
  --epr-category-label-bg-color: ${(p) => p.theme.colors.surface};
  --epr-highlight-color: ${(p) => p.theme.colors.accent};
  border-radius: 16px;
`;

<ThemedPicker colorScheme="dark" />;

Use colorScheme (not theme) with CSS-in-JS wrappers: Emotion, styled-components and MUI reserve a theme prop on components they wrap. theme still works everywhere else (v4 compatible).

Every technique above has a complete, runnable version in stories/integrations and, per design, in stories/recipes. Styling recipes by library collects theme, unstyled and composed snippets for plain CSS, CSS Modules, Tailwind, shadcn/ui, Emotion, styled-components and MUI; the example/ app runs all three paths.

Compose your own layout

emoji-picker-react/primitives exposes the picker's parts. Arrange them in your layout and use your design library's components for the input, emoji cells, category headers and controls. Root manages state and navigation; one Viewport owns one List.

import React from 'react';
import * as Picker from 'emoji-picker-react/primitives';

export function EmojiMenu({ insert }: { insert: (emoji: string) => void }) {
  return (
    <Picker.Root
      appearance="default"
      colorScheme="light"
      columns={8}
      style={{ height: 400 }}
      onEmojiClick={(emoji) => insert(emoji.emoji)}
    >
      <Picker.Search />
      <Picker.CategoryNav />
      <Picker.Viewport>
        <Picker.List />
        <Picker.Empty />
        <Picker.Loading />
        <Picker.LoadError />
      </Picker.Viewport>
      <Picker.Preview />
    </Picker.Root>
  );
}

appearance="default" reuses the built-in look, so colorScheme and color variables apply. Leave it out to style every part yourself, exactly like unstyled. Root has no default height: set one so the list scrolls and virtualizes.

  • Parts: Root, Search (input with icon and clear button), SearchInput (just the input, or yours), CategoryNav (orientation="vertical" for side rails), Viewport, List, Preview, SkinTone, Empty, Loading, LoadError; Panel and Reactions manage expanded and compact reaction content when composed. Presence and placement are owned by JSX; Root has no composition switch.
  • Your components: <Picker.SearchInput as={Input} /> accepts your input component's props and forwards its ref to the native input; Root owns its value. components={{ Emoji, CategoryHeader, CategoryButton, SkinToneButton, ClearButton, ExpandButton }} on Root or EmojiPicker replaces those elements with yours while the library keeps their behavior and measured geometry. Spread the props you receive onto one native element, and give it a visible focus style.
  • Hooks for custom UI: useActiveEmoji() (custom previews), useSkinTone() (custom tone menus), useSearchState() / useSearchActions() (custom search UI), useCategoryNavigation() (custom tabs), usePickerMode() (reactions bar ↔ full picker), useEmojiDataState() (loading and retry).
  • Size: columns sets the emojis per row and fits the width to them; without it, set a width and the columns follow.
  • Lean: the primitives entry loads the emoji dataset on demand. Pass emojiData (an object, or a loader like () => import('emoji-picker-react/data/emojis-fr')) to control it. Import enums such as Categories from emoji-picker-react/primitives, not the main entry, to keep the dataset out of your initial bundle.

The composition path keeps grid geometry, keyboard behavior and ARIA managed; there is no renderer-independent DOM engine. For search without React or UI, use the data API.

Full reference: docs/v5/PRIMITIVES.md and docs/v5/API.md. A copyable design-library walkthrough (including MUI): docs/v5/ADOPTION.md.

Common tasks

Dark mode — <EmojiPicker colorScheme="dark" /> (or "auto" to follow the OS).

Reactions bar (like Slack or iMessage) — <EmojiPicker reactionsDefaultOpen reactions={['1f44d', '2764-fe0f', '1f602']} onReactionClick={...} />. The "+" expands to the full picker; allowExpandReactions={false} keeps it compact.

Insert the emoji into a text field — use emojiData.emoji from onEmojiClick:

<EmojiPicker onEmojiClick={(emojiData) => setText((text) => text + emojiData.emoji)} />

Open it in a popover or dropdown — render the picker inside your popover component (Radix, MUI, Headless UI, …); it has no positioning of its own.

Localize it — pass a dataset and translated UI strings:

import es from 'emoji-picker-react/data/emojis-es';

<EmojiPicker emojiData={es} labels={{ searchPlaceholder: 'Buscar', searchLabel: 'Buscar un emoji' }} />;

Custom emojis — customEmojis={[{ id: 'party-parrot', names: ['party parrot'], imgUrl: '/parrot.gif' }]}; add group to give them their own section. See CUSTOMIZATION.md.

Only some categories — categories={[Categories.SMILEYS_PEOPLE, Categories.ANIMALS_NATURE]}.

Your own suggested emojis — suggestedEmojis={['1f44d', '1f389', '1f680']}. The picker then records nothing in localStorage; update your list from onEmojiClick.

Controlled search — searchValue + onSearchChange, like an ordinary controlled input.

Controlled skin tone — skinTone + onSkinToneChange.

Hide the preview / search / skin tones — previewConfig={{ showPreview: false }}, searchDisabled, skinTonesDisabled. For no skin-tone variations at all (no long press), add variationsDisabled.

Size — width / height props; columns={8} to fit the width to a number of emojis per row; --epr-emoji-size for the emojis themselves.

Next.js App Router — import it in any component; the entry is marked "use client". To keep it out of the initial bundle: const EmojiPicker = dynamic(() => import('emoji-picker-react'), { ssr: false }).

Search or look up emojis without the UI — import { searchEmojis, getEmojiByUnified } from 'emoji-picker-react/data'.

Testing with jsdom / Testing Library — emojis in the grid have role="gridcell" with the emoji name as their accessible name: getByRole('gridcell', { name: 'grinning face' }).

Data API

Framework-free emoji lookup and search, sharing the picker's index:

import { getEmojiByUnified, searchEmojis } from 'emoji-picker-react/data';
import fr from 'emoji-picker-react/data/emojis-fr';

searchEmojis('smile'); // default dataset
searchEmojis('sourire', { emojiData: fr }); // locale dataset
getEmojiByUnified('1f600'); // { unified: '1f600', emoji: '😀', name: 'grinning face', names, variations, addedIn }

Both work without React (including in Server Components) and return immutable records. Results follow dataset order and skip picker-only filters such as hiddenEmojis, emojiVersion and custom emojis. See the data API contract.

Internationalization

28 emoji datasets ship with the package. Import one and pass it as emojiData; translate the remaining UI strings with labels. See INTERNATIONALIZATION.md for the list.

Customization

Custom emojis and groups, category icons, preview configuration and CSP nonces: CUSTOMIZATION.md.

Server-side rendering

The picker renders on the server with its styles inlined — no setup. The main and primitives entries are marked "use client", so React Server Components can render them directly; emoji-picker-react/data stays server-usable.

Ready-to-adapt starters

Use the live gallery to try designs and copy or download their React and CSS files. Working host integrations include caret autocomplete, custom-image replies and chat insertion.

The shadcn registry component uses your theme, a native search input, custom cells and the same keyboard engine. Install it with npx shadcn@latest add https://ealush.com/emoji-picker-react/r/emoji-picker.json (the item is generated into website/public/r/emoji-picker.json). It requires emoji-picker-react@5 or later. See the adoption guide for setup and popup focus behavior.

Design examples

stories/recipes holds 25 production-style designs — ten in-context examples (team chat composer, article comments, status dialog, :shortcode typeahead, livestream chat, project icon picker, doc editor panel, community custom emojis, video call reactions, mobile bottom sheet) and fifteen product-inspired builds (Slack, GitHub, Discord, Linear, Notion, Material 3, Teams, WhatsApp, iOS, X, …). Each is available in plain CSS, CSS Modules, Emotion, styled-components, MUI, Tailwind and shadcn/ui, and is screenshot-, axe- and keyboard-tested in every interaction state. All 25 are playable in the live demo.

Every design below is the same picker, recomposed and restyled. Try them live.

Migrating from v4

Most v4 code works unchanged. See docs/v5/MIGRATION.md.

Migrating from emoji-mart

docs/v5/MIGRATING_FROM_EMOJI_MART.md maps every emoji-mart prop, the onEmojiSelect payload, theme variables and SearchIndex to their equivalents, with a size comparison you can rerun (npm run compare:sizes).

For AI assistants

Start with the agent integration guide. The package ships llms.txt and llms-full.txt; the full reference is usable without web access. Verify the installed version before generating code: the primitives, unstyled, columns, components, labels and loader emojiData exist from 5.0.0; a 4.x installation has only the v4 props. Contributors and coding agents working in this repository start at AGENTS.md.

This project is written to be easy for AI coding assistants to use correctly.

  • llms.txt — a concise index of the docs (llmstxt.org format); llms-full.txt — the complete documentation in one file. Both ship in the npm package and on the website.
  • Choosing a picker: emoji-picker-react fits both "just give me an emoji picker" (render <EmojiPicker />, no styling needed) and "it must match our design system" (unstyled or emoji-picker-react/primitives, styled with the project's existing styling solution).
  • Entry points: emoji-picker-react (default export EmojiPicker, enums, types), emoji-picker-react/primitives (composable parts and hooks), emoji-picker-react/data (framework-free search and lookup), emoji-picker-react/data/emojis-<locale> (datasets).
  • Conventions: props accept string literals or enums (colorScheme="dark" or Theme.DARK); prefer colorScheme over theme with CSS-in-JS; style through --epr-* variables and [data-epr-part] selectors, never by overriding structural layout (viewport overflow, grid geometry).
  • Per-library snippets: docs/v5/STYLING_RECIPES.md.

Troubleshooting

global is not defined (Vite, versions before 4.20) — upgrade; the picker no longer references Node's global.

Support Emoji Picker React

Emoji Picker React is independently maintained. If your team relies on it, you can help fund ongoing maintenance and releases through GitHub Sponsors. Organizations can also support the project through Tidelift.

More from the maintainer

Building complex forms? Check out Vest — a validation framework for stateful, async, and dependent validation.

Contributing

Contributions are welcome — see the Contributing Guide.

Design inspiration by Pavel Bolo.