# v5 API reference Choose batteries included or BYOD (bring your own design, design language and design library). Both use the same behavior engine. The v5 public API is designed from the consumer inward: - `` remains the primary path; - v4 source compatibility is preserved wherever the architecture does not require a break; - new APIs are added only for demonstrated needs; - composition is layered: rearrange the macro structure, read picker state through hooks, and — when a brand needs it — replace the markup of emoji cells and category headers while the library keeps owning their behavior. ## 1. Batteries included ```tsx import EmojiPicker from 'emoji-picker-react'; function Composer() { return ; } ``` Consumers do not need primitives to upgrade to v5. ## 2. Additions to the default picker v5 adds: ```ts type EmojiPickerV5Additions = { searchValue?: string; defaultSearchValue?: string; onSearchChange?: (value: string) => void; searchLabel?: string; suggestedEmojis?: string[]; // §7 keepSearchFocus?: boolean; // §4a variationsDisabled?: boolean; // §10b onReactionsModeChange?: (reactionsOpen: boolean) => void; labels?: Partial; // §5a skinTone?: SkinTonesValue; // §5b, controlled unstyled?: boolean; // §11 columns?: number; // §11a components?: PickerComponents; // shared managed-control replacements emojiData?: EmojiData | EmojiDataLoader; // §12a, loader form is new }; ``` `skinTonePickerLocation` also accepts `SkinTonePickerLocation.NONE` (§8). The assembled picker retains its legacy visibility and placement props; see [PROPS.md](../../PROPS.md). Those switches are not primitive Root props. ## 3. Literal values without removing enums v5 accepts readable literals: ```tsx ``` Existing enum-based code remains valid: ```tsx ``` Existing enum exports remain part of v5. ## 4. Controlled search ```tsx function ControlledPicker() { const [search, setSearch] = useState(''); return ( ); } ``` Uncontrolled: ```tsx ``` The visible value and callback use raw user text. Filtering uses a normalized derived query and is debounced; see [primitive state and actions](./PRIMITIVES.md#state-and-actions). Type-to-search differs slightly between controlled and uncontrolled usage: - both modes: focus Search immediately, then behave like ordinary input editing; - controlled: the keystroke is a proposal like any other edit, so a parent that ignores it simply leaves the value unchanged — the same thing that happens when you type into a controlled input whose parent ignores you; - focus transfer is never conditional on acceptance. If Search is omitted, built-in type-to-search behaves like `searchDisabled`, while an external application input may still drive filtering through controlled `searchValue`. ## 4a. Keep focus in search By default, the arrow key toward the grid moves focus from the search input into it. With `keepSearchFocus`, focus stays in the input and the arrow keys move a highlight instead, so the user can keep typing: ```tsx ``` - 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. - 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. - Once started, the highlight follows the results: when the applied search, dataset or `suggestedEmojis` changes, it moves to the first result and updates Preview. - 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. - 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. - Space types a space, so it does not open skin tone variations in this mode. 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. 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. Primitive compositions pass the same prop to `Root`. The highlight code is a separate chunk that the first picker with `keepSearchFocus` loads when it mounts, so pickers without the prop never load it. Until it arrives, the arrow keys stay in the input; typing and Enter work from the start. If it fails to load (offline, a Content Security Policy, or a deploy that removed it), the arrow keys move focus into the grid as they do without the prop; the next picker mount tries again. Turning `keepSearchFocus` off ends a highlight, and Enter then selects the first result in view, as without the prop. ## 5. Search accessibility label The search input's accessible label, an English constant in v4, is configurable: ```tsx ``` `searchLabel` defaults to the v4 English label, so plug-and-play behavior is unchanged. Primitive consumers may also supply a consumer `aria-label` through Search `inputProps`; when supplied there, that explicit primitive-level label wins for that Search instance. ## 5a. Localize every string `labels` covers every user-facing string the picker renders or announces. Omitted keys keep their English (v4) defaults; `labels` wins over the individual `searchPlaceholder`/`searchLabel`/`searchClearButtonLabel` props, which keep working. ```tsx ``` Category names come from the localized `emojiData` (or `categories`); the preview caption from `previewConfig.defaultCaption`. ## 5b. Controlled skin tone ```tsx const [tone, setTone] = useState(SkinTones.NEUTRAL); ``` While `skinTone` is present it is the source of truth (`defaultSkinTone` is ignored); choosing a tone reports through `onSkinToneChange`. ## 5c. Native emoji support detection `emojiStyle` defaults to `native`. Native glyphs come from the installed emoji font, which can lag behind the dataset. The picker probes that font on the client and hides: - emojis newer than the platform renders (they would show as empty boxes); - individual country flags where the installed font has no flag glyph; - individual missing glyphs, broken join/modifier sequences, and subdivision flags that fall back to a plain black flag. Detection never delays the first paint. Before paint, the picker draws one sample per emoji version and a country flag, and hides versions (and country and subdivision flags) the font cannot draw. Exact checks then run only for identities about to be shown: grid cells within a viewport of the visible rows (in either scroll direction) with their skin tone variants, and the reaction choices. They run in small batches between tasks and are cached, so each glyph is checked at most once per font; the rest of the inventory keeps its version prediction. When a check disagrees with the prediction the picker rerenders; otherwise nothing changes. Pending keyboard or tab navigation is never cancelled by these results. Filtering precedes grid layout, so removed cells leave no gaps; search result counts use the same filtered inventory. Reactions and the variation menu also omit unavailable choices, and the managed Preview does not draw unsupported native glyphs. This applies to both the default picker and primitives. Image styles and image-based custom emojis do not depend on OS glyph support. `emojiVersion` remains an additional maximum version: it never disables native detection or makes unsupported glyphs available. SSR and hydration-first output stay deterministic; detection is client-only. The standalone `Emoji` component outside a picker Root does not participate in Root inventory filtering. Detection is heuristic, not a browser-provided glyph-coverage guarantee. It compares rendered width and canvas artwork under two text colors, which distinguishes ordinary missing-glyph/text fallback from fixed-color emoji artwork, including black-and-white artwork. Unusual fonts may be misclassified. Inconclusive sequence probes preserve those entries. If canvas readback is blocked or no stable emoji artwork baseline is available, the picker preserves the whole inventory rather than hiding everything. Avoid promising complete suppression in those environments. Results are cached per document, font and sequence. Bounded canvas batches share pixel readbacks; later pickers on the same page reuse cached results without probing again. A flag polyfill font can be supplied through `--epr-emoji-font-family`; detection measures that same font. Detection follows font changes selected by attributes on Root or its ancestors (including `class`, `style`, and `data-theme`), even when those fonts are already loaded. It also re-checks in the background after webfonts finish loading, keeping the current results on screen until the new ones are ready. ## 6. Observe reactions/full-picker mode ```tsx { updateLayout(reactionsOpen); }} /> ``` This is an observer, not a second controlled mode API. Existing `reactionsDefaultOpen`, `allowExpandReactions`, `reactions`, `onReactionClick`, and `collapseToReactions()` remain. ## 7. Caller-defined suggestions ```tsx ``` For standard emoji IDs: - matching is case-insensitive; - matching ignores where U+FE0F appears: `'2764'` and `'2764-fe0f'` both resolve to the dataset's own spelling, the same rule `reactions` and `hiddenEmojis` use; - a valid variation remains that exact variation for rendering rather than collapsing to the neutral base emoji; - duplicates are removed by canonical normalized ID, first occurrence wins. 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. 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. When `suggestedEmojis` is supplied, it determines the Suggested category contents/order, and the picker records nothing: picks are not written to the browser's recents (`localStorage`), so an app that owns its recents updates them from `onEmojiClick`. `suggestedEmojisMode` remains relevant only when `suggestedEmojis` is absent. See [primitive state and actions](./PRIMITIVES.md#state-and-actions). ## 8. Structural primitives ```tsx import * as EmojiPicker from 'emoji-picker-react/primitives'; function ProductPicker() { return (
); } ``` Root renders exactly the children supplied by the caller. It never inserts Panel, Reactions, Search, Preview or SkinTone. There is no composition switch. A full-picker-only layout may omit Panel. For compact reactions mode, place Reactions outside Panel and put the expanded content inside Panel so its hidden/inert state follows the mode. ```tsx check import * as React from 'react'; import * as Picker from 'emoji-picker-react/primitives'; export function ReactionPicker({ open }: { open: boolean }) { if (!open) return null; return ( ); } ``` Conditional mounting controls whether a Root exists. Omit Search/SearchInput, Preview or SkinTone to omit that UI; `open`, `searchDisabled`, `skinTonesDisabled`, `skinTonePickerLocation` and `previewConfig.showPreview` belong to the assembled default picker. `composition` and `panelProps` are unsupported. Put layout and native attributes directly on Panel. The full primitive grammar, props, refs, native prop forwarding and validation behavior are documented in [PRIMITIVES.md](./PRIMITIVES.md). ### Exports Parts: - `Root` - `Panel` — expanded content with managed hidden/inert presence - `Reactions` — compact reaction controls - `Search` - `SearchInput` — native input / ref-forwarding design-system input - `LoadError` — localized recoverable loader failure - `CategoryNav` - `Viewport` - `List` - `Preview` - `Empty` — renders while an applied search shows nothing (§10a) - `Loading` — renders while the dataset loads (§12a) - `SkinTone` — the skin tone control, placed anywhere (§10b) Hooks (call inside `Root`): - `useActiveEmoji()` — the hovered/focused emoji, in `onEmojiClick`'s shape - `useSkinTone()` — `[tone, setTone]`; the setter reports `onSkinToneChange` - `useSearchState()` — `{ search, resultCount }` - `useSearchActions()` — `{ setValue, clear }` - `useCategoryNavigation()` — `{ categories, activeCategory, jumpToCategory }` - `usePickerMode()` — `{ reactionsOpen, canExpand, expand, collapse }` - `useEmojiDataState()` — `{ loading, error, retry }` Tokens (plain data): `structuralPickerTokens`, `lightPickerTokens`, `darkPickerTokens`, `defaultPickerTokens`. Panel and Reactions are exported parts. Rendering Reactions opts into the compact UI, while `reactionsDefaultOpen`, `allowExpandReactions` and the mode actions control behavior. Rendering a part never requires a secondary presence switch. ## 9. Custom emoji cells and category headers Branded designs often need different markup, not just different CSS. `List` accepts `components` for emoji cells and category headers. The library still owns their behavior: each component receives the library-owned props and must spread them onto its element. ```tsx import * as EmojiPicker from 'emoji-picker-react/primitives'; // Define components outside render: a new identity remounts every cell. function Cell({ emoji, children, ...props }: EmojiPicker.EmojiRenderProps) { return ( ); } function Header({ category, ...props }: EmojiPicker.CategoryHeaderRenderProps) { return

{category.name}

; } ``` - `Emoji` must render a ` ``` They are not emojis: search, the results count, recents and `onEmojiClick` never see them. They are ordinary tab stops, not cells, so the arrow keys do not reach them. Invalid: ```tsx ``` List requires Viewport. A Viewport without a List renders no grid (development warns). List must be inside Viewport (ideally its direct child; wrappers from styling libraries are fine), optionally accompanied by `Empty` and `Loading`. A second List is rejected. There is no child-ordering rule. Root renders whatever you give it, in the order you gave it. Registered parts must remain inside their Root DOM boundary. Arrow keys follow the layout you compose. Inside a part they move as usual: between cells, tabs or tones, or the caret in the search input. When a key has nowhere to go inside a part, focus moves to the nearest part in that direction on screen. That happens: - past the first or last row of the grid, and from its first or last column when a part sits beside the grid (otherwise Left/Right keep wrapping through the rows); - past the first or last tab, and with the arrows across the tab bar; - with the caret at the edge of the search text, and with Up/Down in the input; - from a closed skin tone control. Arrows with Shift, Ctrl, Alt or Meta held never leave a part: in the search input they extend the selection or move by word, as in any text field. In the grid and the tab bar they move as plain arrows do, but stop at the part's edge (Left and Right keep wrapping through the grid's rows). "Nearest" is measured from the focused element, and focus lands next to it: from a rail tab, the row beside that tab; from a cell, the tab beside or above it. From the search input the grid starts at the first result. A tab bar above, below or beside the grid (a vertical rail, mirrored under `dir="rtl"`), a search input below the grid, or parts reordered with CSS need no configuration. While a search is active, the input and the results are each other's neighbors in whichever direction they face, skipping parts between them. Tab order is unchanged. Escape undoes one thing at a time: it closes an open variation or tone menu, then a `keepSearchFocus` highlight, then clears a non-empty search (and returns to its input). With nothing to undo, the picker leaves Escape alone (no `preventDefault`), so a host popover or dialog closes. The same goes for an Escape right after a controlled parent rejected the clear (its `searchValue` and the input are unchanged): the next Escape reaches the host. ## 10a. Empty state ```tsx {({ search }) =>

No emoji for “{search}”

}
``` Renders only while an applied search shows no emojis (counting exactly what the list shows). Without children it renders `labels.searchResultsNone`. The default picker includes it. ## 10b. Skin tone anywhere ```tsx
``` `SkinTone` is the managed control (keyboard, focus region, callbacks). Only one skin tone control may exist per Root. Search and Preview never insert one automatically; render it wherever your layout needs it. To build a control from scratch instead, use `useSkinTone()`. Leaving SkinTone out does not remove variations from the grid. For no skin tones at all, also pass `variationsDisabled`: no long press, no variation marker, and `hasVariations: false` in custom cells. Space selects the emoji, as Enter does, instead of opening a menu. The active skin tone (`skinTone` / `defaultSkinTone`) still applies. ```tsx ``` ## 10b2. Vertical category rail ```tsx ``` Stacks the tabs and switches keyboard navigation to Up/Down between tabs (Left/Right leave the rail), announced through `aria-orientation`. `SkinTone` takes the same `orientation` prop for its fan. ## 10c. Hooks ```tsx function MyPreview() { const emoji = EmojiPicker.useActiveEmoji(); return
{emoji ? `${emoji.emoji} ${emoji.names[emoji.names.length - 1]}` : 'Pick one'}
; } function MyStatus() { const { search, resultCount } = EmojiPicker.useSearchState(); return search ? {resultCount} matches : null; } ``` Hooks throw in development when called outside `Root`. ## 11. Styling ```tsx ``` Managed descendants and the internal panel expose the deliberately small stable part API described in [PRIMITIVES.md](./PRIMITIVES.md#parts-and-replacements). Styling is opt-in in both directions: - **Primitives are unbranded by default.** Root always applies the geometry tokens (sizes, spacing, stacking), so a bare composition is fully functional; colors are yours. `` opts into the default color tokens (CSS variables only — no border, background or font on Root). - **`colorScheme`, not `theme`.** Emotion, styled-components and MUI reserve a `theme` prop on the components they wrap, so `styled(Root)` / `styled(EmojiPicker)` would swallow it. The default picker accepts both (`colorScheme` wins); Root only takes `colorScheme`. - **The default picker is branded by default.** `` keeps the batteries-included composition and behavior but drops the chrome and colors, for styling from scratch with `className`, `--epr-*` variables and `[data-epr-part]` selectors. ## 11a. Columns ```tsx ``` `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. The intrinsic grid width remains present during an empty search, data loading or a load error. ## 12. Data API The data entry point is defined in [DATA_API.md](./DATA_API.md): ```ts import { getEmojiByUnified, searchEmojis, type EmojiInfo, } from 'emoji-picker-react/data'; ``` The existing top-level `emojiByUnified` export remains unchanged and is **not** replaced by `getEmojiByUnified`. `searchEmojis` is dataset search, not "the exact results currently visible in one picker instance." Picker-only filters such as `emojiVersion`, `hiddenEmojis`, and `customEmojis` remain Root configuration. Each record carries the ready-to-insert `emoji` text and a display `name`. v5 does not promise Slack-shortcode conversion because the dataset does not establish canonical Slack alias semantics. ## 12a. Dataset loading and bundle size `emojiData` accepts an object (synchronous, SSR-safe) or a loader: ```tsx const loadFrench = () => import('emoji-picker-react/data/emojis-fr'); ``` - The default `emoji-picker-react` entry bundles the English dataset and is always synchronous. - The `emoji-picker-react/primitives` entry does **not** load the dataset up front. A Root without `emojiData` loads the bundled English dataset on demand (its own chunk in ESM builds; the configured minimal consumer currently measures about 33 KiB min+gz up front including ShipStyles), rendering `` meanwhile. Pass an object for server rendering. - ESM entries share their implementation chunks, so using the default picker and primitives together ships the implementation once. Hoist loaders (module scope or `useCallback`): a new function identity loads again. Loaders receive `{ signal?: AbortSignal }`; source changes, retries and unmount abort the previous attempt. Zero-argument import loaders remain valid. Rejection and synchronous throws become recoverable state, not an empty successful result. The default picker displays localized error/retry UI. Primitive consumers compose `` next to List/Loading or read `useEmojiDataState()` (`{ loading, error, retry }`). A custom `LoadError` child can be a function receiving `{ error, retry }`. Import runtime `Categories`, `EmojiStyle`, `SkinTones`, `SkinTonePickerLocation`, `SuggestionMode`, and `Theme` from `/primitives` in a lean composition. Importing runtime values from the main or `/data` entry registers its eager default dataset. ## 12b. Exported types Both the main and the primitives entry export the types a typed consumer needs without reaching into the package: `PickerProps` / `RootProps`, `EmojiClickData`, `EmojiClickHandler` and `OnEmojiClickApi` (the `onEmojiClick` / `onReactionClick` signature and its `collapseToReactions` API), `SkinToneChangeHandler`, `PickerLabels`, `PreviewConfig`, `CustomEmoji`, `CategoryConfig`, `CategoryIcons`, `EmojiData`, `EmojiDataLoader` / `EmojiDataLoaderOptions` / `EmojiDataInput`, `PickerComponents` with `EmojiRenderProps`, `CategoryHeaderRenderProps`, `CategoryButtonRenderProps`, `SkinToneButtonRenderProps` and `ListEmoji`, plus the literal unions `ThemeValue`, `EmojiStyleValue`, `SuggestionModeValue` and `SkinTonesValue`. `defaultSkinTone` and `skinTone` accept `SkinTonesValue` (`'neutral'`, `'1f3fb'`, … or the `SkinTones` enum). The main entry exports PickerProps (with Props as its alias); the primitives entry exports RootProps and each part’s prop types. See the entry files for their complete exports. ## 13. Locale imports v4's documentation used: ```ts import es from 'emoji-picker-react/dist/data/emojis-es'; ``` v5 canonicalizes: ```ts import es from 'emoji-picker-react/data/emojis-es'; ``` The documented v4 `dist/data/emojis-*` paths, and the raw `src/data/*.json` datasets, remain working in v5 through deprecated compatibility export aliases. Arbitrary undocumented deep imports do not receive that guarantee. ## 13a. React Server Components The main and primitives entry files are marked `"use client"`, so a Server Component may render `` directly. `emoji-picker-react/data` is not marked and stays callable on the server. ## 14. Error ownership The default `` keeps the current library ErrorBoundary. The primitives Root does not install one. Render/lifecycle errors from consumer UI propagate to the application's surrounding ErrorBoundary when one exists. Event-handler exceptions are not caught by React ErrorBoundaries and follow normal React/browser event behavior. ## 15. Development validation The validation model is intentionally narrow: - render/context checks catch primitive-outside-Root (and hooks called outside Root), List-outside-Viewport, and invalid Viewport children (mode is driven by Root props and public usePickerMode actions; caller JSX controls Panel/Reactions placement); - singleton duplicates are detected by Root registration after mount; - development throws on a second singleton registration; - production keeps the first registration authoritative and warns once; - there is no "missing Viewport after paint" validator because Viewport/List are optional; - SSR performs only render-time/context validation. Exact error text is not semver API. ## Native search, panel styling and active custom cells `SearchInput` forwards its ref and native input props directly to an input. Root owns `searchValue`, `defaultSearchValue` and `onSearchChange`; input `onChange` is an observer. Supply `as={Input}` for a design-system component that forwards its ref and native input props to one real input. Placeholder, autofocus and accessible label can be supplied directly. Use either Search or SearchInput per Root; they share IME handling, region registration and results announcements. Panel accepts className, style, native attributes and handlers directly. Panel owns its hidden/inert presence; its children and layout come from the caller’s JSX. Apply flex/grid/gap classes on Panel itself. Custom `EmojiRenderProps.emoji.isActive` and `data-epr-active` indicate hover or keyboard focus (with `keepSearchFocus`, the keyboard highlight, which also carries `data-epr-highlighted`). Only the previous and next active cells are notified; unrelated cells do not rerender for each hover. Preserve all managed button props and position styles when replacing markup. ## Lifetimes, ownership and design-library inputs Root owns its callback scope even when Roots are nested. The default wrapper may supply fresh callbacks across its memo boundary only to its own Root. Conditionally unmount Root to remove its content and abort pending loading; mounting it again starts a new lifetime. The assembled EmojiPicker retains its `open` prop: closed, it renders nothing but keeps its state, as in v4. Native disabled/read-only search inputs do not accept grid type-to-search proposals. Managed markup cannot be replaced through `dangerouslySetInnerHTML`; structural children, roles and reserved picker attributes remain owned by the library. Stable forwarded refs are retained across unrelated renders, and callback-ref cleanup is supported while retaining the React 16.8 runtime floor. Malformed async loader output enters the same localized error/retry path as a rejected load. SearchInput infers design-library props from its `as` component, including required options, while protecting the Root-owned value and native input contract. Custom List cells and headers receive structural styles without managed decorative appearance. They own their visible focus and design-library styling. Grid row measurement uses the outer border box, so design-library borders do not shrink virtualized row spacing. ## Checked usage examples The `tsx check` blocks in this reference are compiled from the Markdown itself by `npm run check:contracts`. They use the current source API, including JSX-owned presence and placement. ### Default picker and exported types ```tsx check import * as React from 'react'; import EmojiPicker, { type EmojiClickHandler, type EmojiDataLoader, type PickerLabels, } from 'emoji-picker-react'; const labels: Partial = { searchPlaceholder: 'Rechercher' }; const loadFrench: EmojiDataLoader = () => import('emoji-picker-react/data/emojis-fr'); const onPick: EmojiClickHandler = (data, _event, api) => { data.getImageUrl('apple'); api?.collapseToReactions(); }; export function LocalizedPicker() { const [search, setSearch] = React.useState(''); return ; } ``` ### Design-system input and replacement cell ```tsx check import * as React from 'react'; import * as Picker from 'emoji-picker-react/primitives'; const Field = React.forwardRef>( (props, ref) => , ); function Cell({ emoji, ...props }: Picker.EmojiRenderProps) { return {categories.map(category => )} {resultCount} ; } export function ExternalSearchPicker() { return ; } ``` ### Framework-free data entry ```tsx check import { getEmojiByUnified, searchEmojis, type EmojiInfo } from 'emoji-picker-react/data'; import es from 'emoji-picker-react/data/emojis-es'; export const results: readonly EmojiInfo[] = searchEmojis('gato', { emojiData: es }); export const first = getEmojiByUnified(results[0]?.unified ?? '1f431', { emojiData: es }); export const label = first ? `${first.emoji} ${first.name}` : ''; ``` ### Default picker root attributes `EmojiPicker` accepts `id`, `title`, `lang`, `dir`, `aria-*` and `data-*` identifying attributes on its root, alongside `className` and `style`. Its public types include the named attributes and React ARIA attributes. The library reserves `role` and `data-epr-*`; use a wrapper or primitives for other native event handlers.