# Adopting v5
Choose batteries included or BYOD — bring your own design, design language and design library — with one shared picker engine, with custom image emoji, custom groups, reactions, localization and the same keyboard engine in both modes.
## Native search in your design system
```tsx
import * as Picker from 'emoji-picker-react/primitives';
```
`Input` must forward its ref and native input props to one ``. Root controls search; use Root’s `searchValue` / `onSearchChange` for controlled state. SearchInput’s ref/className/placeholder address the input directly. Use Search for the managed input/icon/clear-button region instead. They register the same single search region and share IME, filtering and keyboard behavior.
Custom input props are inferred from `as`, including required design-library options (for example `variant`) and native-name overrides such as `size="sm"`. Native search event handlers keep their input event types. Native tags other than `input` are excluded from the API. Put layout props on an explicit Panel between Root and its expanded parts. Panel owns its collapsed hidden/inert behavior. Custom emoji cells receive `emoji.isActive` and `data-epr-active`; preserve all managed button props and position styles. Custom cells and category headers own decorative styles; they do not inherit the default button reset or header typography. Provide a visible keyboard focus state.
### Inputs with a wrapper ref, including MUI
Some libraries put `ref`, `className` or ARIA props on a wrapper. Adapt them before using `as`: the picker needs the native input for focus, IME and region navigation. For MUI TextField, route native attributes through [`slotProps.htmlInput`](https://mui.com/material-ui/react-text-field/#controlling-the-html-input), and use `inputRef` for the input ref:
```tsx
import * as React from 'react';
import TextField from '@mui/material/TextField';
const MuiInput = React.forwardRef<
HTMLInputElement,
React.InputHTMLAttributes & {
variant: 'outlined' | 'filled';
}
>(function MuiInput(
{ variant, value, onChange, disabled, autoFocus, placeholder, ...native },
ref,
) {
return (
);
});
// Inside Picker.Root:
;
```
Use the slot API of your installed MUI version. Keep the picker-supplied composition handlers, value, type and native attributes on the input. MUI buttons also need `minWidth: 0` and padding compatible with the managed square cell; spread the managed position style and className. Set the library's `color` explicitly when spreading native button props, since MUI narrows that prop to its palette names. The [complete executable MUI composition](../../stories/v5/MuiComposition.tsx) includes bordered Button cells and Typography headers; its types and cross-browser keyboard, measurement and axe behavior are checked in this repository.
## Dataset recovery and full localization
```tsx
const loadFrench = ({ signal }: Picker.EmojiDataLoaderOptions) =>
fetch('/emoji-fr.json', { signal }).then(response => {
if (!response.ok) throw new Error('Dataset request failed');
return response.json();
});
{/* SearchInput, Viewport, List, Loading and LoadError */}
```
Hoist the loader. Retry starts a new attempt; source changes and unmount abort the old attempt. Conditionally mount Root to control loading lifetime; an unmounted Root starts no loads. Malformed loader payloads enter the same recoverable error state. Late successes or failures cannot overwrite a newer source. `useEmojiDataState()` exposes `{ loading, error, retry }` inside Root when an application owns recovery UI.
Emoji names/categories come from any of the 28 bundled datasets or your own data. Translate the remaining visible and announced strings through `labels`, category names through `categories`, and the preview caption through `previewConfig.defaultCaption`. This includes loading failure and retry text.
## shadcn registry
`registry/emoji-picker.tsx` is the consumer component; `npm run registry` embeds it in `website/public/r/emoji-picker.json` and lists it in the flat registry index `website/public/r/registry.json`, both of which the website publishes. Install it with:
```sh
npx shadcn@latest add https://ealush.com/emoji-picker-react/r/emoji-picker.json
```
The registry item depends on `emoji-picker-react@^5.0.0`. The registry component styles its search input, tabs, emoji cells, headers, tone button and variations menu with your shadcn theme classes (`bg-popover`, `bg-accent`, `border-input`, …) through `components` and `[data-epr-part]` variants, so it follows light, dark and custom themes. The picker keeps behavior and cell geometry. Wrap it in your existing Popover:
```tsx
😊 {
insertAtCaret(emoji);
setOpen(false);
}} />
```
The host owns insertion and dismissal. The Popover restores trigger focus. Escape first closes an open variation/tone menu, then a subsequent Escape reaches the host. The light/dark Storybook examples import the registry component itself; browser checks exercise search, keyboard insertion, dismissal, focus restoration and WCAG-tagged axe rules. `npm run test:e2e` also runs the install command itself: the shadcn CLI adds the item from the locally generated registry into a fresh Tailwind v4 app, which must render with the app's theme tokens under a strict CSP.
## Working starter sources
The website gallery’s “Get the code” control fetches React/CSS/CSS Modules source only when requested. Copy or download the composition and styles, then adapt the host callback:
- Shortcode typeahead: editable textarea, query at the caret, token replacement, suffix preservation, ArrowDown/Enter acceptance and Escape dismissal. This is name autocomplete; canonical Slack alias conversion is not promised.
- Community reply: custom images select a host-owned `:id:` token at the textarea selection.
- Team chat: inserts at the input selection and closes the picker.
All 25 designs share a source generator. Their seven styling-stack variants remain covered by visual equivalence tests.
## Startup accounting
Import runtime constants from `/primitives`. Runtime imports from the main or `/data` entry register the synchronous default dataset, so those mixed graphs intentionally have a larger initial payload.
The configured minimal consumer, bundled from an installed package tarball, must stay within **35 KiB initial gzip including ShipStyles**, with the dataset deferred. Unused parts, icons and styles can be removed by the consumer bundler; the default picker retains its complete composition. The complete-runtime regression cap is **35 KiB**: down from 41 KiB after the original 40.5 KiB adoption candidate to 34 KiB, then raised to 35 KiB in 5.1.3 to keep layered stylesheets mounted (closing a `cssLayer` picker no longer re-lays out the page). Earlier gates that excluded ShipStyles are not comparable. Reducing the runtime toward 25 KiB remains profiling work. Account for initial JavaScript and dataset traffic separately.