emoji-picker-react 5.1.0 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ export declare const OVERSCAN_ROWS = 2;
2
+ export declare function isJustBelowViewport(top: number, bottom: number, size: number): boolean;
1
3
  export declare function shouldVirtualize({ scrollTop, clientHeight, topOffset, style, dimensions, }: {
2
4
  scrollTop: number;
3
5
  clientHeight: number;
package/llms-full.txt CHANGED
@@ -53,23 +53,24 @@ Contents:
53
53
  1. Agent integration guide (docs/v5/AGENT_GUIDE.md): installed-version discovery, batteries included/BYOD, typed input/cell contracts, localization and verification
54
54
  2. Copyable integration prompts (docs/v5/PROMPTS.md): setup, brand styling, design-library composition, recipe adoption and v4-to-v5 migration
55
55
  3. README (README.md): overview, quick start, choosing a path, styling with any solution, common tasks
56
- 4. Props reference (PROPS.md): every prop of the default <EmojiPicker />
57
- 5. CSS variables (CSS_VARIABLES.md): every --epr-* design token
58
- 6. Customization (CUSTOMIZATION.md): custom emojis and groups, category icons, preview, CSP nonce
59
- 7. Internationalization (INTERNATIONALIZATION.md): locale datasets and labels
60
- 8. Adoption (docs/v5/ADOPTION.md): native input, loading recovery, registry installation and source downloads
61
- 9. v5 API (docs/v5/API.md): all v5 additions with examples
62
- 10. Primitives (docs/v5/PRIMITIVES.md): composable parts, hooks, custom cells, grammar
63
- 11. Styling (docs/v5/STYLING.md): tokens, parts, cascade, structural rules
64
- 12. Styling recipes by library (docs/v5/STYLING_RECIPES.md): theme, unstyled and composed snippets for plain CSS, CSS Modules, Tailwind, shadcn/ui, Emotion, styled-components and MUI
65
- 13. Data API (docs/v5/DATA_API.md): framework-free search and lookup
66
- 14. Migrating from v4 (docs/v5/MIGRATION.md): what changed and how to upgrade
67
- 15. Migrating from emoji-mart (docs/v5/MIGRATING_FROM_EMOJI_MART.md): prop and payload mapping, size comparison, CSP
68
- 16. Website examples (website/README.md): typed customization, locale loading and composition
69
- 17. Design recipes (stories/recipes/README.md): 25 designs, each in seven styling stacks
70
- 18. Styling integrations (stories/integrations/README.md): shadcn/ui, Tailwind, Emotion, styled-components, CSS Modules, CSS, MUI
71
- 19. Example app (example/README.md): Vite + React 19 app running the batteries-included, unstyled, composed and data-API paths
72
- 20. Worked example: team chat picker composition (stories/recipes/team-chat/picker.tsx; picker only, demo app chrome excluded)
56
+ 4. Emoji picker comparison (docs/v5/COMPARISON.md): v5 versus Emoji Mart, Frimousse and emoji-picker-element: capabilities, v5 APIs and audited sources
57
+ 5. Props reference (PROPS.md): every prop of the default <EmojiPicker />
58
+ 6. CSS variables (CSS_VARIABLES.md): every --epr-* design token
59
+ 7. Customization (CUSTOMIZATION.md): custom emojis and groups, category icons, preview, CSP nonce
60
+ 8. Internationalization (INTERNATIONALIZATION.md): locale datasets and labels
61
+ 9. Adoption (docs/v5/ADOPTION.md): native input, loading recovery, registry installation and source downloads
62
+ 10. v5 API (docs/v5/API.md): all v5 additions with examples
63
+ 11. Primitives (docs/v5/PRIMITIVES.md): composable parts, hooks, custom cells, grammar
64
+ 12. Styling (docs/v5/STYLING.md): tokens, parts, cascade, structural rules
65
+ 13. Styling recipes by library (docs/v5/STYLING_RECIPES.md): theme, unstyled and composed snippets for plain CSS, CSS Modules, Tailwind, shadcn/ui, Emotion, styled-components and MUI
66
+ 14. Data API (docs/v5/DATA_API.md): framework-free search and lookup
67
+ 15. Migrating from v4 (docs/v5/MIGRATION.md): what changed and how to upgrade
68
+ 16. Migrating from emoji-mart (docs/v5/MIGRATING_FROM_EMOJI_MART.md): prop and payload mapping, size comparison, CSP
69
+ 17. Website examples (website/README.md): typed customization, locale loading and composition
70
+ 18. Design recipes (stories/recipes/README.md): 25 designs, each in seven styling stacks
71
+ 19. Styling integrations (stories/integrations/README.md): shadcn/ui, Tailwind, Emotion, styled-components, CSS Modules, CSS, MUI
72
+ 20. Example app (example/README.md): Vite + React 19 app running the batteries-included, unstyled, composed and data-API paths
73
+ 21. Worked example: team chat picker composition (stories/recipes/team-chat/picker.tsx; picker only, demo app chrome excluded)
73
74
 
74
75
  <!-- source: docs/v5/AGENT_GUIDE.md -->
75
76
 
@@ -463,7 +464,7 @@ One emoji-picker engine, with two ways to build your UI: use the complete picker
463
464
 
464
465
  [![npm downloads](https://img.shields.io/npm/dm/emoji-picker-react.svg)](https://www.npmjs.com/package/emoji-picker-react)
465
466
 
466
- **[Live demo](https://ealush.com/emoji-picker-react)** · **[Quick start](#quick-start)** · **[Props](PROPS.md)** · **[BYOD](#byod-bring-your-own-design)** · **[Common tasks](#common-tasks)** · **[For AI assistants](#for-ai-assistants)** · **[Report a bug](https://github.com/ealush/emoji-picker-react/issues)** · **[Sponsor](https://github.com/sponsors/ealush)**
467
+ **[Live demo](https://ealush.com/emoji-picker-react)** · **[Quick start](#quick-start)** · **[Props](PROPS.md)** · **[Compare pickers](#compare-emoji-pickers)** · **[BYOD](#byod-bring-your-own-design)** · **[Common tasks](#common-tasks)** · **[For AI assistants](#for-ai-assistants)** · **[Report a bug](https://github.com/ealush/emoji-picker-react/issues)** · **[Sponsor](https://github.com/sponsors/ealush)**
467
468
 
468
469
  ![image](https://github.com/ealush/emoji-picker-react/assets/11255103/48901306-e7fd-49cd-8f1e-9b214083a61d)
469
470
 
@@ -524,6 +525,34 @@ See [PROPS.md](PROPS.md) for every prop.
524
525
 
525
526
  > **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](docs/v5/MIGRATION.md). The APIs on this page need `emoji-picker-react@5` or later.
526
527
 
528
+ ## Compare emoji pickers
529
+
530
+ v5 supports both a complete styled picker and unstyled React composition,
531
+ with custom image emoji, recents, category navigation and reactions built in.
532
+
533
+ | Capability | emoji-picker-react v5 | Emoji Mart + React wrapper | Frimousse | emoji-picker-element |
534
+ | --- | --- | --- | --- | --- |
535
+ | Complete styled picker | Built in | Built in | Compose/style parts or use shadcn starter | Built in |
536
+ | Unstyled React primitives | Built in | No React primitives API | Built in | Web component |
537
+ | Keyboard navigation, search and skin tones | Built in | Built in | Built in | Built in |
538
+ | Localized search and UI | Built in | Built in | Built in | Built in |
539
+ | Native glyph support filtering | Built in | Built in | Built in for default data resolver | Built in |
540
+ | Custom image emoji and named groups | Built in | Built in | Custom data and cell rendering | Built in |
541
+ | Recent/frequent emoji | Built in | Built in | App-owned | Built-in favorites |
542
+ | Category navigation controls | Built in | Built in | App-owned | Built in |
543
+ | Compact reactions bar with expansion | Built in | App-owned | App-owned | App-owned |
544
+ | Standalone emoji search API | Framework-free `/data` entry | `SearchIndex` | Metadata lookup only | Browser `Database` API |
545
+ | Persistent dataset cache | Remote cache policy is app-owned | Remote cache policy is app-owned | Built-in localStorage cache | Built-in IndexedDB cache |
546
+
547
+ “App-owned” means the application implements the feature. Custom renderers and
548
+ data resolvers can extend the supplied behavior. Bundled data in
549
+ emoji-picker-react and Emoji Mart can be used offline without a remote cache.
550
+
551
+ Audited October 20, 2026 against Emoji Mart 5.6.0 / React wrapper 1.1.1,
552
+ Frimousse 0.4.0 and emoji-picker-element 1.29.1. See the
553
+ [full comparison and sources](docs/v5/COMPARISON.md) for API details,
554
+ v5 capabilities, native-detection limits and data-loading APIs.
555
+
527
556
  ## Copyable integration prompts
528
557
 
529
558
  Use the [setup, customization, recipe adoption and v5 migration prompts](docs/v5/PROMPTS.md)
@@ -878,6 +907,132 @@ Contributions are welcome — see the [Contributing Guide](https://github.com/ea
878
907
 
879
908
  Design inspiration by [Pavel Bolo](https://pavelbolo.com).
880
909
 
910
+ <!-- source: docs/v5/COMPARISON.md -->
911
+
912
+ <!-- Emoji picker comparison -->
913
+
914
+ # Comparing emoji pickers
915
+
916
+ **Audited October 20, 2026.** This comparison covers **emoji-picker-react v5**
917
+ in this repository, Emoji Mart (`emoji-mart` 5.6.0 with `@emoji-mart/react`
918
+ 1.1.1), Frimousse 0.4.0 and emoji-picker-element 1.29.1. Competitor sources
919
+ below are pinned to their published release commits. Earlier comparisons of
920
+ emoji-picker-react v4 do not describe v5's unstyled mode, primitives, custom
921
+ components, controlled state or data loaders.
922
+
923
+ Emoji Mart is an established complete picker with a React wrapper; Frimousse
924
+ is a direct comparison for unstyled React composition; emoji-picker-element
925
+ provides a framework-independent web-component alternative. PicMo is excluded
926
+ from the table because its [repository was archived in September 2023](https://github.com/joeattardi/picmo).
927
+
928
+ ## Capability comparison
929
+
930
+ **Built in** means a supported public API. **App-owned** means the application
931
+ supplies that feature; it is not a built-in picker API in the audited version.
932
+ Custom renderers and data resolvers can extend a library beyond its defaults.
933
+ The table compares capabilities, not identical prop names or component trees.
934
+
935
+ | Capability | emoji-picker-react v5 | [Emoji Mart][mart-readme] + React wrapper | [Frimousse][frimousse-api] | [emoji-picker-element][element-readme] |
936
+ | --- | --- | --- | --- | --- |
937
+ | Complete styled picker | Built in: `<EmojiPicker />`, no CSS import | Built in | Compose and style parts; shadcn starter available | Built in: `<emoji-picker>` |
938
+ | 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 |
939
+ | 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 |
940
+ | 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 |
941
+ | Keyboard navigation and accessible semantics | Built in | Built in | Built in | Built in |
942
+ | List rendering strategy | Virtualized rows | Visibility-based row rendering | Virtualized rows | Category panels; custom-category rendering containment |
943
+ | Controlled search from application state | `searchValue` / `onSearchChange`; search actions hook | No documented controlled-search prop | Controlled Search `value` / `onChange` | No documented controlled-search prop |
944
+ | Hovered/focused emoji preview | `Preview` or `useActiveEmoji()` | Built-in preview with position options | `ActiveEmoji` or `useActiveEmoji()` | App-owned preview |
945
+ | 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 |
946
+ | 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` |
947
+ | 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 |
948
+ | 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 |
949
+ | Recent/frequent emoji | Built in; also accepts supplied suggestions | Built-in frequent category | App-owned | Built-in favorites |
950
+ | 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 |
951
+ | 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 |
952
+ | Compact reactions bar that expands to the picker | Built in; also composable with `Reactions` / `Panel` | App-owned | App-owned | App-owned |
953
+ | 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` |
954
+ | Custom loading and empty states | `Loading`, `Empty` | App-owned loading UI; configurable no-results appearance | `Loading`, `Empty` | Built-in loading/error messages via `i18n` |
955
+ | Recoverable data-error UI | `LoadError`, retry hook, abortable loader | App-owned | App-owned | Built-in error message; no documented retry control |
956
+ | 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 |
957
+ | 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 |
958
+ | 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 |
959
+ | License | MIT | MIT | MIT | Apache-2.0 |
960
+
961
+ All four provide keyboard behavior and accessibility support. This audit
962
+ reviews APIs and source; it does not rank screen-reader quality or certify
963
+ accessibility. v5's [browser accessibility tests](../../playwright/a11y-reactions-focus.spec.ts)
964
+ exercise keyboard focus and axe checks.
965
+ Native-glyph detection is heuristic in every picker; see v5's
966
+ [detection limits](./API.md#5c-native-emoji-support-detection).
967
+
968
+ ## Capabilities in v5
969
+
970
+ v5 provides a complete styled picker and composable primitives backed by the
971
+ same engine. Choose the supplied layout, remove its appearance with `unstyled`,
972
+ or compose your own controls and layout. Search, virtualization, keyboard
973
+ navigation and localization stay built in across these paths.
974
+
975
+ | Capability | emoji-picker-react v5 API |
976
+ | --- | --- |
977
+ | Complete styled picker | Render `<EmojiPicker />`; theme it with `colorScheme` and `--epr-*` tokens |
978
+ | Custom layout | Compose `Root`, `SearchInput` (or `Search`), `Viewport` and `List` from `/primitives` |
979
+ | Design-system styling | Use classes, styles and `[data-epr-part]` selectors, or install the [shadcn registry component](./ADOPTION.md#shadcn-registry) |
980
+ | Custom input, emoji cells and headers | Use `SearchInput as={Input}`, Root `components` or `List components`; preserve managed props, refs and geometry |
981
+ | Controlled search | Use Root's `searchValue` / `onSearchChange`, or `useSearchActions()` for custom controls |
982
+ | Active-emoji previews | Compose `Preview` or build a custom preview with `useActiveEmoji()` |
983
+ | Skin-tone controls | Compose `SkinTone`, control `skinTone` / `onSkinToneChange`, or use `useSkinTone()` |
984
+ | Localized and asynchronous data | Supply an `emojiData` object or abortable loader; translate remaining strings through `labels` |
985
+ | Loading, empty and error states | Compose `Loading`, `Empty` and `LoadError`; access loading state and retry through `useEmojiDataState()` |
986
+ | Custom image emoji and groups | Supply `customEmojis` with image URLs and optional named groups |
987
+ | Recent/frequent emoji and supplied suggestions | Configure `suggestedEmojisMode`, or supply `suggestedEmojis` |
988
+ | Category navigation | Compose `CategoryNav` or build controls with `useCategoryNavigation()` |
989
+ | Compact reactions with expansion | Use reactions mode on `<EmojiPicker />`, or compose `Reactions` and `Panel` with `usePickerMode()` |
990
+ | Search and metadata outside React | Use `searchEmojis` and `getEmojiByUnified` from `/data`, passing a localized dataset when needed |
991
+
992
+ See the [primitives contract](./PRIMITIVES.md), [input adapters and data
993
+ recovery](./ADOPTION.md), [styling recipes](./STYLING_RECIPES.md) and
994
+ [data API](./DATA_API.md) for implementation patterns.
995
+
996
+ ## Audit sources and maintenance
997
+
998
+ The emoji-picker-react column was checked against this checkout's
999
+ [public props](../../PROPS.md), [primitive types](../../src/primitives/types.ts),
1000
+ [component replacement types](../../src/primitives/components.tsx),
1001
+ [data API](./DATA_API.md) and [localization guide](../../INTERNATIONALIZATION.md).
1002
+ Competitor versions were verified against npm's `latest` metadata on the audit
1003
+ date, then their release sources were inspected:
1004
+
1005
+ - **Emoji Mart:** [5.6.0 README][mart-readme], [actual picker options][mart-props],
1006
+ [row rendering][mart-rendering], [native filtering][mart-native] and
1007
+ [React wrapper 1.1.1 peer dependencies][mart-react]. The separate
1008
+ `@emoji-mart/data` package was 1.2.1. Where the README and runtime defaults
1009
+ differ, the table follows the source.
1010
+ - **Frimousse:** [0.4.0 README][frimousse-readme], [public types][frimousse-api],
1011
+ [exports][frimousse-exports], [data resolver][frimousse-data],
1012
+ [persistent cache][frimousse-cache] and [metadata lookup][frimousse-details].
1013
+ - **emoji-picker-element:** [1.29.1 README and database API][element-readme],
1014
+ [picker behavior][element-picker] and [rendering styles][element-styles].
1015
+
1016
+ Recheck these versions and public contracts when updating the table. A missing
1017
+ first-class API is not evidence that a capability is impossible to implement.
1018
+ No competitor browser benchmark or manual assistive-technology comparison was
1019
+ performed for this documentation audit.
1020
+
1021
+ [mart-readme]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/README.md
1022
+ [mart-props]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/components/Picker/PickerProps.ts
1023
+ [mart-rendering]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/components/Picker/Picker.tsx
1024
+ [mart-native]: https://github.com/missive/emoji-mart/blob/8af68308851a0585c5224fb16f574ca92f4f578c/packages/emoji-mart/src/helpers/native-support.ts
1025
+ [mart-react]: https://github.com/missive/emoji-mart/blob/465eec076b23d55b6606b6a81fe3a4fd7b2375a4/packages/emoji-mart-react/package.json
1026
+ [frimousse-readme]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/README.md
1027
+ [frimousse-api]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/types.ts
1028
+ [frimousse-exports]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/index.ts
1029
+ [frimousse-data]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/emoji.ts
1030
+ [frimousse-cache]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/emoji-data-cache.ts
1031
+ [frimousse-details]: https://github.com/liveblocks/frimousse/blob/868aebea0b441be44005e67c90ab6e082756a906/src/data/get-emoji-details.ts
1032
+ [element-readme]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/README.md
1033
+ [element-picker]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/src/picker/components/Picker/Picker.js
1034
+ [element-styles]: https://github.com/nolanlawson/emoji-picker-element/blob/ded142e5c820c3fe6803ae372dd2b77ed7fbcf9d/src/picker/styles/picker.scss
1035
+
881
1036
  <!-- source: PROPS.md -->
882
1037
 
883
1038
  <!-- Props reference -->
@@ -1570,12 +1725,14 @@ By default, the arrow key toward the grid moves focus from the search input into
1570
1725
  ```
1571
1726
 
1572
1727
  - The arrow toward the grid highlights the first emoji in view (the first result while searching): ArrowDown when the grid is below the input, ArrowUp when it is above, and Left or Right when it is beside the input (with the caret at that end of the text). The arrow keys then move the highlight through the grid, including rows that are not rendered yet. Otherwise Left and Right edit the text until an emoji is highlighted; from then on they move the highlight, and Escape gives them back to the text. With Shift, Ctrl, Alt or Meta, arrows always stay with the input (selecting text, moving by word), with or without a highlight. Left and Right follow the reading direction in RTL.
1573
- - Enter selects the highlighted emoji, or the first result when nothing is highlighted, even if the results are scrolled. Focus stays in the input.
1574
- - Once started, the highlight follows the results: when the search changes, it moves to the first result.
1728
+ - Enter selects the highlighted emoji, or the first result when nothing is highlighted, even if the results are scrolled. Focus stays in the input. While filtering is pending, Enter queues one selection for the matching results after they commit. Editing the query, pressing another key, or leaving the input cancels that queued selection.
1729
+ - Once started, the highlight follows the results: when the applied search, dataset or `suggestedEmojis` changes, it moves to the first result and updates Preview.
1575
1730
  - Moving past the grid edge that faces the input (ArrowUp from the first row when the input is above, Left from the first column when it is on the left), Escape, or leaving the input removes the highlight, and cancels a move that has not landed yet. Escape ends only the highlight, even one whose emoji is scrolled out and unmounted; with nothing highlighted, it clears the search as usual.
1576
1731
  - Hovering an emoji previews it without taking focus from the input (true in both modes: hover never takes focus from the search input). The highlight stays where it is, and Enter still selects it; once the pointer leaves the emoji, the preview shows the highlighted one again.
1577
1732
  - Space types a space, so it does not open skin tone variations in this mode.
1578
1733
 
1734
+ An Escape consumed by the picker stops propagation in window capture, before document-capture and bubbling host dismissal handlers. With nothing left to undo, Escape is left untouched for the host.
1735
+
1579
1736
  The input becomes an ARIA combobox with a grid popup: `aria-controls` names the grid while both are mounted, and `aria-activedescendant` names the highlighted cell (it is empty while virtualization has the highlighted cell scrolled out). That cell carries `data-epr-highlighted` and is the active emoji for Preview, `useActiveEmoji()` and `emoji.isActive`. The default appearance styles it like keyboard focus; with `unstyled` or custom cells, style `[data-epr-highlighted]` as well.
1580
1737
 
1581
1738
  Primitive compositions pass the same prop to `Root`.
@@ -1686,7 +1843,7 @@ For standard emoji IDs:
1686
1843
  - a valid variation remains that exact variation for rendering rather than collapsing to the neutral base emoji;
1687
1844
  - duplicates are removed by canonical normalized ID, first occurrence wins.
1688
1845
 
1689
- Custom emoji IDs use the same case-insensitive rule — `customEmojis` are already lowercased when indexed, so there is no separate exact-match pass.
1846
+ Custom emoji IDs use the same case-insensitive rule: IDs are lowercased when indexed. Custom display names retain the supplied capitalization; search still matches them case-insensitively.
1690
1847
 
1691
1848
  Entries may be unified IDs, custom emoji IDs, or the emoji characters themselves (`'🧠'`, `'❤️'`), so recents stored as inserted text can be passed through unchanged; unknown entries are ignored.
1692
1849
 
@@ -1978,6 +2135,8 @@ Styling is opt-in in both directions:
1978
2135
 
1979
2136
  `columns` sets the emojis per row on EmojiPicker and Root. The picker's width then fits those columns plus a stable scrollbar gutter, unless the consumer sets a width (`width`, `style` or any class). A container narrower than the columns renders fewer columns instead of clipping. Without `columns`, the width decides the column count, as in v4.
1980
2137
 
2138
+ The intrinsic grid width remains present during an empty search, data loading or a load error.
2139
+
1981
2140
  ## 12. Data API
1982
2141
 
1983
2142
  The data entry point is defined in [DATA_API.md](./DATA_API.md):
@@ -2451,6 +2610,7 @@ The following variables are already documented public customization surface and
2451
2610
  - `--epr-preview-text-color`
2452
2611
 
2453
2612
  ### Native emoji font
2613
+ - `--epr-font-family` — font for picker text (`sans-serif` by default). Set it to `inherit` on the picker to use the surrounding application's font. Native emoji glyphs keep their separate font stack.
2454
2614
  - `--epr-emoji-font-family` (v5) — font stack for native emojis, e.g. a country-flag polyfill font; native support detection measures this same font.
2455
2615
 
2456
2616
  ### Skin tone
package/llms.txt CHANGED
@@ -51,6 +51,7 @@ searchEmojis('party'); getEmojiByUnified('1f389'); // { emoji: '🎉', name: 'pa
51
51
  - [Agent integration guide](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/docs/v5/AGENT_GUIDE.md): installed-version discovery, batteries included/BYOD, typed input/cell contracts, localization and verification
52
52
  - [Copyable integration prompts](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/docs/v5/PROMPTS.md): setup, brand styling, design-library composition, recipe adoption and v4-to-v5 migration
53
53
  - [README](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/README.md): overview, quick start, choosing a path, styling with any solution, common tasks
54
+ - [Emoji picker comparison](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/docs/v5/COMPARISON.md): v5 versus Emoji Mart, Frimousse and emoji-picker-element: capabilities, v5 APIs and audited sources
54
55
  - [Props reference](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/PROPS.md): every prop of the default <EmojiPicker />
55
56
  - [CSS variables](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/CSS_VARIABLES.md): every --epr-* design token
56
57
  - [Customization](https://raw.githubusercontent.com/ealush/emoji-picker-react/master/CUSTOMIZATION.md): custom emojis and groups, category icons, preview, CSP nonce
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.1.0",
2
+ "version": "5.1.1",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -1,9 +0,0 @@
1
- import {
2
- registerDefaultEmojiData
3
- } from "./chunk-FJVAS6PR.mjs";
4
- import {
5
- emojis_default
6
- } from "./chunk-ZOAKI5M6.mjs";
7
-
8
- // src/data/registerDefaultEmojiData.ts
9
- registerDefaultEmojiData(emojis_default);
@@ -1,214 +0,0 @@
1
- // src/data/defaultEmojiData.ts
2
- var registered = null;
3
- function registerDefaultEmojiData(data) {
4
- registered = data;
5
- }
6
- function getRegisteredDefaultEmojiData() {
7
- return registered;
8
- }
9
- var EMPTY_EMOJI_DATA = Object.freeze({
10
- categories: {},
11
- emojis: {}
12
- });
13
- function defaultEmojiDataOrEmpty() {
14
- return registered ?? EMPTY_EMOJI_DATA;
15
- }
16
- var pending = null;
17
- function loadDefaultEmojiData() {
18
- if (registered) {
19
- return Promise.resolve(registered);
20
- }
21
- if (!pending) {
22
- pending = import("./emojis-VTEL5M5C.mjs").then((module) => {
23
- const data = module.default ?? module;
24
- registered = registered ?? data;
25
- return registered;
26
- }).catch((error) => {
27
- pending = null;
28
- throw error;
29
- });
30
- }
31
- return pending;
32
- }
33
-
34
- // src/dataUtils/parseNativeEmoji.ts
35
- function parseNativeEmoji(unified) {
36
- return unified.split("-").map((hex) => String.fromCodePoint(parseInt(hex, 16))).join("");
37
- }
38
-
39
- // src/data-core/prepare.ts
40
- var coreCache = /* @__PURE__ */ new WeakMap();
41
- var prepareCount = 0;
42
- function normalizeUnified(input) {
43
- return input.trim().toLowerCase();
44
- }
45
- function normalizeQuery(input) {
46
- if (!input || typeof input !== "string") {
47
- return "";
48
- }
49
- return input.trim().toLowerCase();
50
- }
51
- function toEmojiInfo(unified, names, variations, addedIn) {
52
- return Object.freeze({
53
- unified,
54
- emoji: parseNativeEmoji(unified),
55
- name: names[names.length - 1] || "",
56
- names: Object.freeze([...names]),
57
- variations: Object.freeze([...variations ?? []]),
58
- addedIn
59
- });
60
- }
61
- function readStringArray(raw) {
62
- if (!Array.isArray(raw)) {
63
- return [];
64
- }
65
- return raw.filter((n) => typeof n === "string").map(
66
- (n) => n.toLowerCase()
67
- );
68
- }
69
- function appendEntry(byUnified, byCharMutable, records, entry) {
70
- const rawUnified = entry.u;
71
- if (typeof rawUnified !== "string" || rawUnified.length === 0) {
72
- return;
73
- }
74
- const unified = rawUnified.toLowerCase();
75
- if (byUnified.has(unified)) {
76
- return;
77
- }
78
- const names = readStringArray(entry.n);
79
- const variations = readStringArray(entry.v);
80
- const rawAddedIn = entry.a;
81
- const addedIn = typeof rawAddedIn === "string" ? rawAddedIn : String(rawAddedIn ?? "");
82
- const info = toEmojiInfo(unified, names, variations, addedIn);
83
- records.push(info);
84
- byUnified.set(unified, info);
85
- for (const variation of info.variations) {
86
- if (!byUnified.has(variation)) {
87
- byUnified.set(variation, info);
88
- }
89
- }
90
- indexCharBucket(byCharMutable, names, info);
91
- }
92
- function indexCharBucket(byCharMutable, names, info) {
93
- const distinctChars = Array.from(new Set(names.join("")));
94
- for (const char of distinctChars) {
95
- const bucket = byCharMutable.get(char);
96
- if (bucket) {
97
- bucket.push(info);
98
- } else {
99
- byCharMutable.set(char, [info]);
100
- }
101
- }
102
- }
103
- function getPreparedCore(emojiData) {
104
- const source = emojiData ?? defaultEmojiDataOrEmpty();
105
- const cached = coreCache.get(source);
106
- if (cached) {
107
- return cached;
108
- }
109
- prepareCount += 1;
110
- const byUnified = /* @__PURE__ */ new Map();
111
- const byCharMutable = /* @__PURE__ */ new Map();
112
- const records = [];
113
- const groups = source.emojis ?? {};
114
- for (const key of Object.keys(groups)) {
115
- const list = groups[key] ?? [];
116
- for (const entry of list) {
117
- appendEntry(byUnified, byCharMutable, records, entry);
118
- }
119
- }
120
- const byChar = /* @__PURE__ */ new Map();
121
- byCharMutable.forEach((bucket, char) => {
122
- byChar.set(char, Object.freeze(bucket));
123
- });
124
- const core = {
125
- records,
126
- byUnified,
127
- byChar,
128
- queryMemo: /* @__PURE__ */ new Map()
129
- };
130
- coreCache.set(source, core);
131
- return core;
132
- }
133
-
134
- // src/data-core/search.ts
135
- var EMPTY_RESULTS = Object.freeze([]);
136
- var MAX_QUERY_MEMO_SIZE = 500;
137
- function memoizeQuery(core, normalized, results) {
138
- const memo = core.queryMemo;
139
- if (memo.has(normalized)) {
140
- memo.delete(normalized);
141
- } else if (memo.size >= MAX_QUERY_MEMO_SIZE) {
142
- const oldest = memo.keys().next();
143
- if (!oldest.done) {
144
- memo.delete(oldest.value);
145
- }
146
- }
147
- memo.set(normalized, results);
148
- }
149
- function smallestCandidateBucket(core, normalized) {
150
- let smallest = core.records;
151
- for (const char of Array.from(normalized)) {
152
- const bucket = core.byChar.get(char);
153
- if (!bucket) {
154
- return EMPTY_RESULTS;
155
- }
156
- if (bucket.length < smallest.length) {
157
- smallest = bucket;
158
- }
159
- }
160
- return smallest;
161
- }
162
- function getEmojiByUnified(unified, options) {
163
- if (!unified || typeof unified !== "string") {
164
- return void 0;
165
- }
166
- const normalized = normalizeUnified(unified);
167
- if (!normalized) {
168
- return void 0;
169
- }
170
- const core = getPreparedCore(options?.emojiData);
171
- return core.byUnified.get(normalized);
172
- }
173
- function searchEmojis(query, options) {
174
- const normalized = normalizeQuery(query);
175
- if (!normalized) {
176
- return EMPTY_RESULTS;
177
- }
178
- const core = getPreparedCore(options?.emojiData);
179
- const memoized = core.queryMemo.get(normalized);
180
- if (memoized) {
181
- memoizeQuery(core, normalized, memoized);
182
- return Object.freeze([...memoized]);
183
- }
184
- if (normalized.length === 1) {
185
- const bucket = core.byChar.get(normalized) ?? EMPTY_RESULTS;
186
- memoizeQuery(core, normalized, bucket);
187
- return Object.freeze([...bucket]);
188
- }
189
- const matches = [];
190
- for (const record of smallestCandidateBucket(core, normalized)) {
191
- const names = record.names;
192
- for (let i = 0; i < names.length; i += 1) {
193
- if (names[i].includes(normalized)) {
194
- matches.push(record);
195
- break;
196
- }
197
- }
198
- }
199
- memoizeQuery(core, normalized, matches);
200
- return Object.freeze([...matches]);
201
- }
202
-
203
- export {
204
- registerDefaultEmojiData,
205
- getRegisteredDefaultEmojiData,
206
- EMPTY_EMOJI_DATA,
207
- defaultEmojiDataOrEmpty,
208
- loadDefaultEmojiData,
209
- parseNativeEmoji,
210
- normalizeQuery,
211
- getPreparedCore,
212
- getEmojiByUnified,
213
- searchEmojis
214
- };