EmojiPicker
EmojiPicker is a full React Native emoji keyboard: 1,923 emojis in 9 categories, keyword search, skin tones, recently used, a category bar, and filtering by Emoji version so older devices never show empty boxes. Emojis render with the system font; no images or fonts are bundled.
import { EmojiPicker } from '@elik56a/react-native-pickers';
<> <Text style={{ fontSize: 24, minHeight: 36, marginBottom: 8 }}> {text} </Text> <EmojiPicker onSelect={(emoji) => setText((t) => t + emoji)} /> </>
Try the props
categoryBarPositioncolumns<EmojiPicker onSelect={insert} />
onSelect
onSelect: (emoji: string, data: Emoji) => void
emoji is the character to insert, with the current skin tone applied. data is the base emoji's metadata:
interface Emoji {
emoji: string; // base character, e.g. '👍'
name: string; // 'thumbs up'
keywords: string[]; // search tags
category: EmojiCategory;
version: number; // Emoji version it was introduced in, e.g. 13.1
skins?: string[]; // 5 skin-tone variants (light → dark) when supported
}
Categories
type EmojiCategory = 'smileys' | 'people' | 'animals' | 'food' | 'travel' | 'activities' | 'objects' | 'symbols' | 'flags';
categories filters and orders the sections and the category bar:
<EmojiPicker height={300} categories={['food', 'animals', 'activities']} onSelect={() => {}} showSkinTones={false} />
Hide specific characters with exclude={['🖕', '🍆']}.
Search
The search field matches names and keywords, ranks name-prefix matches first, and requires every word to match (cat face → 🐱). Search across a custom list in your own UI with searchEmojis.
Skin tones
The ✋ button next to search opens a tone menu. Tones are numbers:
type SkinTone = 0 | 1 | 2 | 3 | 4 | 5; // 0 = default yellow, 1–5 = light → dark
Tones apply to every emoji that supports them, in the grid and in what onSelect returns. Control or persist the tone:
const [tone, setTone] = useState<SkinTone>(0);
<EmojiPicker skinTone={tone} onSkinToneChange={setTone} onSelect={insert} />
Use defaultSkinTone for uncontrolled, or showSkinTones={false} to hide the button.
Recently used
Picked emojis go to a Recently used section at the top (up to maxRecent, default 24, most recent first). The list stores base emojis, so they re-render in whatever tone is current.
In EmojiPickerSheet and EmojiPickerField, the sheet keeps them, so they last between openings. A pick that closes the sheet shows up under Recently used the next time it opens, not while it closes, so the grid doesn't jump as it fades or slides away, as on the iOS emoji keyboard.
The picker keeps recents in memory by default. To persist them across launches, control them:
import AsyncStorage from '@react-native-async-storage/async-storage';
function Picker({ onPick }: { onPick: (e: string) => void }) {
const [recent, setRecent] = useState<string[]>([]);
useEffect(() => {
AsyncStorage.getItem('emoji-recent').then((v) => v && setRecent(JSON.parse(v)));
}, []);
return (
<EmojiPicker
recent={recent}
onRecentChange={(next) => {
setRecent(next);
AsyncStorage.setItem('emoji-recent', JSON.stringify(next));
}}
onSelect={onPick}
/>
);
}
Seed a starter set with defaultRecent={['👍', '❤️', '😂']}, or turn the section off with showRecent={false}.
Older devices: maxEmojiVersion
Emojis newer than the device's font render as empty boxes (common on older Android). Hide them by version:
import { Platform } from 'react-native';
<EmojiPicker
maxEmojiVersion={Platform.OS === 'android' && Platform.Version < 30 ? 12 : undefined}
onSelect={insert}
/>
Designs
Like every picker, it follows the design of the platform it runs on, after that platform's own emoji picker:
ios | material | web | |
|---|---|---|---|
| Categories | at the bottom, like the iOS emoji keyboard | at the bottom, like Gboard | on top |
| Current category | a gray rounded fill | a tinted pill | an underline |
| Search | a short gray search bar | a tall pill | an outlined input with a focus ring |
| Section titles | UPPERCASE | Sentence case | Sentence case |
Use the iOS / Android / Web switch on any example above to compare. categoryBarPosition="top" or "bottom" puts the bar in one place on every platform.
Layout
<EmojiPicker height={340} columns={7} emojiSize={30} categoryBarPosition="bottom" showSectionHeaders={false} onSelect={() => {}} />
columns(default 8) sets emojis per row. Cells are square and fill the width.emojiSizesets the font size (default about 62% of the cell).height(default 380) is the total height including search and category bar.categoryBarPosition:'auto'(default) puts the bar where the design does, at the bottom on iOS and Android and on top on the web;'top'or'bottom'fixes it. Hide the bar withshowCategoryBar={false}.showSectionHeaders={false}removes the "Smileys & Emotion" style headers.
Translating labels and icons
Every label is replaceable. The categoryLabels keys are the categories plus recent and search. Icons can be strings (rendered as text) or any React node:
<EmojiPicker height={320} searchPlaceholder="Rechercher" emptyText="Aucun emoji" categoryLabels={{ recent: 'Récents', smileys: 'Smileys et émotions', people: 'Personnes', animals: 'Animaux et nature', food: 'Nourriture', travel: 'Voyages', activities: 'Activités', objects: 'Objets', symbols: 'Symboles', flags: 'Drapeaux', search: 'Résultats', }} categoryIcons={{ smileys: '🙂', food: '🥐' }} onSelect={() => {}} />
Custom emoji cells
renderEmoji(emoji, data, size) replaces the character, for example to render a custom image set like Twemoji:
<EmojiPicker
renderEmoji={(emoji, data, size) => <Image source={{ uri: twemojiUrl(emoji) }} style={{ width: size, height: size }} />}
onSelect={insert}
/>
In a sheet
<EmojiPickerSheet visible={open} onClose={() => setOpen(false)} onSelect={(e) => setText((t) => t + e)} />
The sheet closes after each pick by default (closeOnSelect={false} to keep it open). → EmojiPickerSheet
In a form field
EmojiPickerField opens the sheet from a field and shows value, the emoji you keep in state. Use renderTrigger for an icon button instead of a field. Switch presentation in the panel: auto opens a dropdown under the field on the web and a bottom sheet on iOS and Android.
presentation<EmojiPickerField
label="Icon"
placeholder="Pick an icon"
value={icon}
onSelect={setIcon}
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
onSelect | (emoji: string, data: Emoji) => void | required | Called with the toned character and its metadata. |
columns | number | 8 | Emojis per row. |
emojiSize | number | ~62% of cell | Emoji font size. |
height | number | 380 | Total height. |
categories | EmojiCategory[] | all 9 | Which categories to show, in order. |
exclude | string[] | — | Characters to hide. |
maxEmojiVersion | number | — | Hide emojis newer than this Emoji version. |
searchable | boolean | true | Show the search field. |
searchPlaceholder | string | 'Search emoji' | Search hint. |
emptyText | string | 'No emoji found' | Empty search result text. |
showCategoryBar | boolean | true | Show the category tabs. |
categoryBarPosition | 'auto' | 'top' | 'bottom' | 'auto' | Tab bar position. auto: bottom in the iOS and Material designs, top on the web. |
showSectionHeaders | boolean | true | Section title rows. |
categoryLabels | Partial<Record<EmojiCategory | 'recent' | 'search', string>> | English | Section and tab labels. |
categoryIcons | Partial<Record<EmojiCategory | 'recent', ReactNode>> | emoji icons | Tab icons. |
skinTone | SkinTone | — | Controlled tone. |
defaultSkinTone | SkinTone | 0 | Initial tone. |
onSkinToneChange | (tone: SkinTone) => void | — | Tone picked in the menu. |
showSkinTones | boolean | true | Show the tone button. |
recent | string[] | — | Controlled recents (base characters). |
defaultRecent | string[] | [] | Initial recents. |
onRecentChange | (recent: string[]) => void | — | New recents after each pick. |
showRecent | boolean | true | Show and update recents. |
maxRecent | number | 24 | Recents kept. |
renderEmoji | (emoji: string, data: Emoji, size: number) => ReactNode | — | Custom cell content. |
style | StyleProp<ViewStyle> | — | Container style. |
styles | SlotStyles<EmojiPickerSlot> | — | Slots below. |
theme | DeepPartial<PickerTheme> | — | Theme override. |
design | 'ios' | 'material' | 'web' | from PickersProvider | The look to follow, over the provider's: iOS, Material or web. |
testID | string | — | Test ID. |
Slots
| Slot | Element |
|---|---|
container | Outer view |
header | Search + tone row |
searchInput | Search TextInput |
skinToneButton / skinToneMenu | Tone button / menu |
categoryBar | Tab bar |
categoryTab / activeCategoryTab | Tabs |
categoryIndicator | Active-tab underline (web design; iOS and Material mark the tab itself) |
sectionHeader / sectionHeaderText | Section titles |
emojiCell / emojiText | Grid cells |
empty / emptyText | No search results |
Data and utilities
The emoji data comes from emojibase (MIT) and is parsed lazily on first use. The helpers are exported for your own UIs (reactions bars, :shortcode autocomplete): getAllEmojis, searchEmojis, findEmoji, withSkinTone, EMOJI_CATEGORIES, DEFAULT_CATEGORY_LABELS, DEFAULT_CATEGORY_ICONS. → Emoji utilities
Related
- Chat input recipe
- EmojiPickerField: the picker behind a form field