# Comparing emoji pickers
**Audited October 20, 2026.** This comparison covers **emoji-picker-react v5**
in this repository, Emoji Mart (`emoji-mart` 5.6.0 with `@emoji-mart/react`
1.1.1), Frimousse 0.4.0 and emoji-picker-element 1.29.1. Competitor sources
below are pinned to their published release commits. Earlier comparisons of
emoji-picker-react v4 do not describe v5's unstyled mode, primitives, custom
components, controlled state or data loaders.
Emoji Mart is an established complete picker with a React wrapper; Frimousse
is a direct comparison for unstyled React composition; emoji-picker-element
provides a framework-independent web-component alternative. PicMo is excluded
from the table because its [repository was archived in September 2023](https://github.com/joeattardi/picmo).
## Capability comparison
**Built in** means a supported public API. **App-owned** means the application
supplies that feature; it is not a built-in picker API in the audited version.
Custom renderers and data resolvers can extend a library beyond its defaults.
The table compares capabilities, not identical prop names or component trees.
| Capability | emoji-picker-react v5 | [Emoji Mart][mart-readme] + React wrapper | [Frimousse][frimousse-api] | [emoji-picker-element][element-readme] |
| --- | --- | --- | --- | --- |
| Complete styled picker | Built in: ``, no CSS import | Built in | Compose and style parts; shadcn starter available | Built in: `` |
| Unstyled React primitives and custom layout | Built in: `/primitives`; `unstyled` also keeps the supplied layout | No React primitives API; configurable web component | Built in | No React primitives API; web component |
| Custom input, emoji cells and category headers | `SearchInput as`, `components`, `List components` | Configurable icons and appearance; no React input/cell slots | Native Search props; List component replacements | CSS/custom shadow styles; no React input/cell slots |
| Styling with Tailwind, CSS Modules or CSS-in-JS | Classes, styles, parts, tokens, optional cascade layer | CSS variables and shadow-root styles | Classes, styles and part attributes | CSS variables and shadow-root styles |
| Keyboard navigation and accessible semantics | Built in | Built in | Built in | Built in |
| List rendering strategy | Virtualized rows | Visibility-based row rendering | Virtualized rows | Category panels; custom-category rendering containment |
| Controlled search from application state | `searchValue` / `onSearchChange`; search actions hook | No documented controlled-search prop | Controlled Search `value` / `onChange` | No documented controlled-search prop |
| Hovered/focused emoji preview | `Preview` or `useActiveEmoji()` | Built-in preview with position options | `ActiveEmoji` or `useActiveEmoji()` | App-owned preview |
| Skin tones and custom tone controls | `SkinTone`, controlled state, `useSkinTone()` | Built-in selector and `skin` option | `SkinToneSelector`, `SkinTone`, `useSkinTone()` | Built-in selector, change event and database preference API |
| Localized emoji search and UI | 28 packaged datasets; `labels` and category/preview configuration | `locale`, `i18n` and supplied data | 28 default locales; native props, custom content and data resolver | `locale`, `i18n` and localized `dataSource` |
| Hide unsupported native glyphs | Version/flag probes plus checks of visible sequences and variants | Native version/flag detection | Default resolver detects version/flag support | Native support detection, including ZWJ checks |
| Category navigation controls | `CategoryNav`, custom buttons, navigation hook | Built-in navigation and icon options | App-owned; no built-in category navigation API | Built-in category tabs |
| Recent/frequent emoji | Built in; also accepts supplied suggestions | Built-in frequent category | App-owned | Built-in favorites |
| Custom image emoji and named groups | Built in: `customEmojis` | Built in: `custom` categories | Custom data/cell rendering; no built-in image-emoji API | Built in: `customEmoji` categories |
| Native and vendor image sets | Native, Apple, Google, Facebook, Twitter; URL override | Native, Apple, Google, Facebook, Twitter; spritesheet override | Native; other rendering through custom cells | Native/custom font; custom image emoji |
| Compact reactions bar that expands to the picker | Built in; also composable with `Reactions` / `Panel` | App-owned | App-owned | App-owned |
| Bundled or self-hosted data | Packaged datasets, object input or async loader | Separate data package, object input or async loader | Default CDN; alternate URL or sync/async resolver | Fetch from configurable `dataSource` |
| Custom loading and empty states | `Loading`, `Empty` | App-owned loading UI; configurable no-results appearance | `Loading`, `Empty` | Built-in loading/error messages via `i18n` |
| Recoverable data-error UI | `LoadError`, retry hook, abortable loader | App-owned | App-owned | Built-in error message; no documented retry control |
| Persistent emoji-dataset cache | Bundled data works offline; remote cache policy is app-owned | Bundled data works offline; remote cache policy is app-owned | Built-in localStorage cache/revalidation; `createEmojiDataCache` for custom data | Built-in IndexedDB cache/revalidation; IndexedDB required |
| Search/metadata outside the picker | Framework-free `/data`: search and unified-code lookup | `SearchIndex` and native-emoji lookup after `init` | `getEmojiDetails` / `useEmojiDetails`; no standalone search API | Separate browser `Database`: search and lookup |
| React compatibility | React 16.8–19 | Wrapper declares React 16.8, 17 and 18 | React 18 and 19 | Framework-independent; integrate as a custom element |
| License | MIT | MIT | MIT | Apache-2.0 |
All four provide keyboard behavior and accessibility support. This audit
reviews APIs and source; it does not rank screen-reader quality or certify
accessibility. v5's [browser accessibility tests](../../playwright/a11y-reactions-focus.spec.ts)
exercise keyboard focus and axe checks.
Native-glyph detection is heuristic in every picker; see v5's
[detection limits](./API.md#5c-native-emoji-support-detection).
## Capabilities in v5
v5 provides a complete styled picker and composable primitives backed by the
same engine. Choose the supplied layout, remove its appearance with `unstyled`,
or compose your own controls and layout. Search, virtualization, keyboard
navigation and localization stay built in across these paths.
| Capability | emoji-picker-react v5 API |
| --- | --- |
| Complete styled picker | Render ``; theme it with `colorScheme` and `--epr-*` tokens |
| Custom layout | Compose `Root`, `SearchInput` (or `Search`), `Viewport` and `List` from `/primitives` |
| Design-system styling | Use classes, styles and `[data-epr-part]` selectors, or install the [shadcn registry component](./ADOPTION.md#shadcn-registry) |
| Custom input, emoji cells and headers | Use `SearchInput as={Input}`, Root `components` or `List components`; preserve managed props, refs and geometry |
| Controlled search | Use Root's `searchValue` / `onSearchChange`, or `useSearchActions()` for custom controls |
| Active-emoji previews | Compose `Preview` or build a custom preview with `useActiveEmoji()` |
| Skin-tone controls | Compose `SkinTone`, control `skinTone` / `onSkinToneChange`, or use `useSkinTone()` |
| Localized and asynchronous data | Supply an `emojiData` object or abortable loader; translate remaining strings through `labels` |
| Loading, empty and error states | Compose `Loading`, `Empty` and `LoadError`; access loading state and retry through `useEmojiDataState()` |
| Custom image emoji and groups | Supply `customEmojis` with image URLs and optional named groups |
| Recent/frequent emoji and supplied suggestions | Configure `suggestedEmojisMode`, or supply `suggestedEmojis` |
| Category navigation | Compose `CategoryNav` or build controls with `useCategoryNavigation()` |
| Compact reactions with expansion | Use reactions mode on ``, or compose `Reactions` and `Panel` with `usePickerMode()` |
| Search and metadata outside React | Use `searchEmojis` and `getEmojiByUnified` from `/data`, passing a localized dataset when needed |
See the [primitives contract](./PRIMITIVES.md), [input adapters and data
recovery](./ADOPTION.md), [styling recipes](./STYLING_RECIPES.md) and
[data API](./DATA_API.md) for implementation patterns.
## Audit sources and maintenance
The emoji-picker-react column was checked against this checkout's
[public props](../../PROPS.md), [primitive types](../../src/primitives/types.ts),
[component replacement types](../../src/primitives/components.tsx),
[data API](./DATA_API.md) and [localization guide](../../INTERNATIONALIZATION.md).
Competitor versions were verified against npm's `latest` metadata on the audit
date, then their release sources were inspected:
- **Emoji Mart:** [5.6.0 README][mart-readme], [actual picker options][mart-props],
[row rendering][mart-rendering], [native filtering][mart-native] and
[React wrapper 1.1.1 peer dependencies][mart-react]. The separate
`@emoji-mart/data` package was 1.2.1. Where the README and runtime defaults
differ, the table follows the source.
- **Frimousse:** [0.4.0 README][frimousse-readme], [public types][frimousse-api],
[exports][frimousse-exports], [data resolver][frimousse-data],
[persistent cache][frimousse-cache] and [metadata lookup][frimousse-details].
- **emoji-picker-element:** [1.29.1 README and database API][element-readme],
[picker behavior][element-picker] and [rendering styles][element-styles].
Recheck these versions and public contracts when updating the table. A missing
first-class API is not evidence that a capability is impossible to implement.
No competitor browser benchmark or manual assistive-technology comparison was
performed for this documentation audit.
[mart-readme]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/README.md
[mart-props]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/components/Picker/PickerProps.ts
[mart-rendering]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/components/Picker/Picker.tsx
[mart-native]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/helpers/native-support.ts
[mart-react]: https://github.com/missive/emoji-mart/blob/465eec076b23d55b6606b6a81fe3a4fd7b2375a4/packages/emoji-mart-react/package.json
[frimousse-readme]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/README.md
[frimousse-api]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/types.ts
[frimousse-exports]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/index.ts
[frimousse-data]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/emoji.ts
[frimousse-cache]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/emoji-data-cache.ts
[frimousse-details]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/get-emoji-details.ts
[element-readme]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/README.md
[element-picker]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/src/picker/components/Picker/Picker.js
[element-styles]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/src/picker/styles/picker.scss