# React Emoji Picker: Suche, Hautfarben und Bottom Sheet

> Bau einen React Emoji Picker mit gewichteter Suche, Hautfarben, zuletzt genutzten Emojis und Bottom Sheet. Und was 1.870 Emojis kosten: 31 KB mit gzip.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 6. Nov. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-emoji-picker
- Englische Fassung: https://wingo-ui.com/blog/react-emoji-picker

## Kurz gesagt

Ein guter React Emoji Picker hat eine gewichtete Suche nach Stichwörtern, Kategorie-Tabs, eine Zeile mit zuletzt genutzten Emojis, Hautfarben, die gespeichert bleiben, eine Vorschau mit dem Namen, ein virtualisiertes Raster, volle Tastaturbedienung und auf dem Smartphone ein Bottom Sheet. Die Daten mitzubündeln kostet für die 1.870 Emojis im Picker von Wingo UI etwa 31 KB mit gzip und funktioniert offline; lädst du sie von einem CDN, bleiben sie aus deinem Bundle, dafür kommt beim ersten Öffnen eine Anfrage dazu. So oder so: Lade den Picker per Lazy Loading, damit Seiten, auf denen ihn niemand öffnet, nichts dafür bezahlen.

Ein React Emoji Picker sieht nach einem Raster voller Smileys aus, bis du selbst einen baust. Wer „party“ tippt, erwartet 🎉 an erster Stelle. Die Emojis, die man ständig nutzt, sollen oben stehen. Die eigene Hautfarbe soll auf jede Hand angewendet werden, ohne dass man sie jedes Mal neu wählt. Und geöffnet wird der Picker oft aus einem Nachrichtenfeld auf dem Smartphone, wo die Tastatur schon die halbe Höhe belegt. Dann fragt noch jemand, warum die Chat-Seite 30 KB schwerer geworden ist. Dieser Guide geht jeden dieser Punkte mit dem [Emoji Picker](https://wingo-ui.com/components/emoji-picker) von Wingo UI durch. Wir bauen diese Bibliothek, sind also nicht neutral; Fakten von außen verlinken auf ihre Quellen und tragen ein Datum.

## Was muss ein React Emoji Picker können?

Acht Dinge: eine gewichtete Suche, Kategorie-Tabs, eine Zeile mit zuletzt genutzten Emojis, Hautfarben, eine Vorschau, ein virtualisiertes Raster, Tastaturbedienung und ein Layout fürs Smartphone. Prüf jede Emoji Picker Komponente gegen diese Liste:

- **Stichwortsuche mit Ranking.** Ein Treffer auf ein Wort des Namens („party popper“) schlägt einen Treffer auf ein Stichwort, und der schlägt einen Treffer mitten in einem Wort. Bei gleich guten Treffern kommen die beliebten Emojis zuerst, „heart“ liefert also ❤️ vor 💟.
- **Kategorie-Tabs**, die zu einem Abschnitt springen und aufleuchten, wenn du an ihm vorbeiscrollst.
- **Eine Zeile mit zuletzt genutzten Emojis** ganz oben, die zwischen zwei Besuchen erhalten bleibt.
- **Hautfarben**, einmal gewählt und überall angewendet.
- **Eine Vorschau** mit dem Emoji in groß, seinem Namen und einem Shortcode wie `:party_popper:`, denn bei 24px sehen sich viele Emojis zum Verwechseln ähnlich.
- **Ein virtualisiertes Raster.** 1.870 Buttons sind eine Menge, wenn sie bei jedem Öffnen eines Popovers gemountet werden; render nur die Zeilen, die gerade sichtbar sind.
- **Tastaturbedienung**: Pfeiltasten, Pos1, Ende sowie Bild auf / Bild ab im Raster, Enter zum Auswählen, Escape leert die Suche, und wer einen Buchstaben tippt, landet wieder in der Suche.
- **Ein Layout fürs Smartphone**: Zellen mit 44px und ein Bottom Sheet statt eines schwebenden Panels.

Der Picker von Wingo UI erfüllt alle acht Punkte. Sein Raster nutzt `@tanstack/react-virtual`, die Zellen wachsen bei einem groben Zeiger (Touch) auf 44px, und die Anzahl der Treffer wird für Screenreader in einer Live-Region angesagt.

## Solltest du die Emoji-Daten mitbündeln oder nachladen?

Bündle sie mit, wenn der Picker sofort aufgehen und offline funktionieren muss; lade sie nach, wenn die Bytes aus deinem JavaScript herausbleiben sollen. Jede Emoji-Auswahl in React steht vor dieser Entscheidung, und die Bibliotheken treffen sie unterschiedlich. Wir haben unsere eigene Datendatei am 6. November 2026 gemessen: `emoji-picker-data.ts` enthält 1.870 Emojis mit ihren englischen CLDR-Namen und Stichwörtern aus Unicode Emoji 15.0, bei 108 KB Quelltext, 31 KB mit gzip und 26 KB mit Brotli. Die Emojis sind Text, den die Systemschrift zeichnet, es lädt also kein einziges Bild.

| Bibliothek (Stand 6. November 2026) | Woher die Daten kommen | Styling |
|---|---|---|
| Wingo UI Emoji Picker | Mitgeliefert in einer Quelldatei, die dir gehört, beim ersten Einsatz geparst | Design Tokens, hell und dunkel |
| [Frimousse](https://frimousse.liveblocks.io/) 0.4.0 | Standardmäßig von Emojibase auf jsDelivr geladen und lokal gecacht; `emojibaseUrl` zeigt auf deine eigene Kopie | Ungestylte Bausteine |
| [emoji-mart](https://github.com/missive/emoji-mart) (`@emoji-mart/react` 1.1.1, zuletzt veröffentlicht im Januar 2023) | Deine Wahl: `@emoji-mart/data` importieren oder aus dem Netz laden | Gestylt, mit Themes |
| [emoji-picker-react](https://github.com/ealush/emoji-picker-react) 5.1.4 | Im Paket enthalten; standardmäßig native Emojis, Bild-Stile laden von jsDelivr | Gestylt, mit Themes, dazu Headless-Bausteine |

Mitbündeln heißt: Der Picker öffnet sofort, funktioniert offline und hängt nie davon ab, dass ein fremdes CDN erreichbar ist. Nachladen hält die Bytes aus deinem JavaScript heraus und lässt dich Emojis aktualisieren, ohne Code auszuliefern; der Preis ist eine Anfrage (und ein Spinner) beim ersten Öffnen. Das README von emoji-mart nennt für seine zwei Modi dieselbe Abwägung. Wenn du eine emoji-picker-react Alternative wegen der Daten oder des Stylings suchst, fällt die Entscheidung in dieser Tabelle.

Wichtiger als die Seite, für die du dich entscheidest: Lade nichts davon auf Seiten, auf denen niemand den Picker öffnet. Unser Picker parst die Zeilen erst beim ersten Einsatz, aber die Datei steckt trotzdem im Chunk, der sie importiert. Lade die Komponente also per Lazy Loading.

```tsx
"use client"

import { useState } from "react"
import dynamic from "next/dynamic"
import { SmilePlus } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Popover } from "@/components/ui/popover"

// picker und daten laden beim ersten öffnen, nicht mit der seite
const EmojiPicker = dynamic(() => import("@/components/ui/emoji-picker"), {
  ssr: false,
  loading: () => <p className="p-4 text-sm text-muted-foreground">Emojis werden geladen…</p>,
})

export function LazyEmojiButton({ onPick }: { onPick: (emoji: string) => void }) {
  const [open, setOpen] = useState(false)
  return (
    <Popover
      open={open}
      onOpenChange={setOpen}
      size="auto"
      padding="none"
      sheetTitle="Emoji hinzufügen"
      trigger={
        <Button variant="ghost" size="icon" radius="full" aria-label="Emoji hinzufügen">
          <SmilePlus />
        </Button>
      }
    >
      <EmojiPicker
        variant="plain"
        onSelect={({ emoji }) => {
          onPick(emoji)
          setOpen(false)
        }}
      />
    </Popover>
  )
}
```

Das [Popover](https://wingo-ui.com/components/popover) mountet seinen Inhalt erst beim Öffnen, also startet `next/dynamic` den Import beim ersten Tipp. Dafür siehst du einmal pro Besuch kurz einen Ladezustand, und auf dem Smartphone zeigt diese Variante den Picker im Sheet mit der eingestellten `height`. Soll er sofort da sein und das Sheet ausfüllen, importier `EmojiPickerPopover` direkt und nimm die 31 KB auf dieser Route in Kauf.

## Wie fügst du den Emoji Picker in eine React-App ein?

Installier ihn mit der CLI. Sie kopiert `emoji-picker.tsx` und die Datendatei nach `components/ui/`, dazu alles, was die beiden importieren (Button, Popover, Tooltip und ein paar Hooks). Der Picker ist ein Pro-Item, die CLI braucht also ein Pro-Konto (`npx wingo-ui@latest login`):

```bash
npx wingo-ui@latest add emoji-picker
```

`EmojiPicker` rendert inline; `EmojiPickerPopover` ergänzt einen Trigger und öffnet den Picker in einem Popover. Beide melden eine Auswahl über `onSelect`, das `{ emoji, base, name, keywords, category, skinTones }` bekommt: das Emoji mit angewendeter Hautfarbe, das Emoji ohne sie und seine Daten. Brauchst du nur den String, bekommt `onValueChange` allein das Emoji.

```tsx
import { EmojiPicker } from "@/components/ui/emoji-picker"

export function StatusEmoji({ onChange }: { onChange: (emoji: string) => void }) {
  return (
    <EmojiPicker
      columns={9}
      size="md"
      height={296}
      maxRecent={24}
      onSelect={({ emoji }) => onChange(emoji)}
    />
  )
}
```

Such nach „pizza“ oder „rocket“, fahr mit der Maus über ein Emoji, um es in der Vorschau unten zu sehen, öffne dann den Hand-Button und wähl eine Hautfarbe:

> Live-Demo (Emoji Picker): Such nach einem Emoji, fahr über eines, um seinen Namen zu lesen, wähl dann eine Hautfarbe und sieh zu, wie sich jede Hand ändert. Probier sie unter [Emoji Picker](https://wingo-ui.com/components/emoji-picker) aus und installier sie mit `npx wingo-ui@latest add emoji-picker`.

Jeder sichtbare Teil ist ein Prop: `size` (Zellen mit 32, 36 oder 44px), `radius`, `tone` für die Tönung der Auswahl, `variant="plain"`, wenn der Picker in einer Card sitzt, die schon eine Füllung hat, sowie `showTabs`, `showPreview`, `showSkinTones` und `categories`, um ihn abzuspecken. Die zuletzt genutzten Emojis und die Hautfarbe landen in `localStorage` unter `storageKey` (Standard `emoji-picker`). Gib einen leeren String mit, damit sie nur im Speicher bleiben, oder steuere sie über `recent` und `skinTone`, wenn du Einstellungen auf dem Server speicherst.

## Wie fügst du in einem Nachrichtenfeld ein Emoji an der Cursorposition ein?

Merk dir die Cursorposition im Feld, solange es den Fokus hat, und setz das Emoji dann genau dort in den Text. Kurze Beispiele, auch unsere, hängen das Emoji ans Ende des Texts. Das ist falsch, sobald jemand den Cursor verschiebt:

```tsx
"use client"

import { useRef, useState } from "react"
import { EmojiPickerPopover } from "@/components/ui/emoji-picker"

export function MessageComposer() {
  const [text, setText] = useState("")
  const fieldRef = useRef<HTMLTextAreaElement>(null)
  const caret = useRef<{ start: number; end: number } | null>(null)

  const remember = () => {
    const field = fieldRef.current
    if (field) caret.current = { start: field.selectionStart, end: field.selectionEnd }
  }

  const insert = (emoji: string) => {
    const { start, end } = caret.current ?? { start: text.length, end: text.length }
    setText(text.slice(0, start) + emoji + text.slice(end))
    caret.current = { start: start + emoji.length, end: start + emoji.length }
  }

  return (
    <div className="flex items-end gap-1 rounded-3xl bg-foreground/5 p-1 ps-4">
      <textarea
        ref={fieldRef}
        value={text}
        rows={1}
        aria-label="Nachricht"
        placeholder="Nachricht an Emma Carter"
        onChange={(event) => setText(event.target.value)}
        onSelect={remember}
        onBlur={remember}
        className="min-h-9 flex-1 resize-none bg-transparent py-2 text-base outline-hidden md:text-sm"
      />
      <EmojiPickerPopover side="top" align="end" closeOnSelect={false} onSelect={({ emoji }) => insert(emoji)} />
    </div>
  )
}
```

`closeOnSelect={false}` hält den Picker für mehrere Emojis hintereinander offen, so nutzt man ihn im Chat, und die Cursor-Ref rückt weiter, damit jedes neue Emoji hinter dem letzten landet. Achte auf `emoji.length`: 👍🏽 besteht aus 4 UTF-16-Einheiten, also muss die Cursor-Rechnung die Länge des Strings nehmen, niemals 1.

## Wie verhält sich ein Emoji Picker auf dem Smartphone?

Er sollte als Bottom Sheet mit 44px-Zellen aufgehen und die Tastatur geschlossen lassen, bis du sie brauchst. Auf dem Smartphone gehen drei Dinge schief, und für jedes gibt es eine Lösung.

**Ein schwebendes Panel passt nicht.** Ein 300px breites Popover, das an einem 44px-Button hängt, verdeckt den Daumen, der es geöffnet hat, und kämpft mit der Tastatur um Platz. Unter 768px öffnet `EmojiPickerPopover` ein Bottom Sheet, das du nach unten ziehen kannst, und der Picker füllt es mit `min(64dvh, 520px)` und so vielen 44px-Spalten, wie in die Breite passen. Das Sheet bekommt unten ein Padding von `env(safe-area-inset-bottom)`, damit die letzte Zeile über dem Home-Indikator bleibt. Über das Sheet selbst haben wir in [React Bottom Sheet fürs Web](https://wingo-ui.com/de/blog/react-bottom-sheet-for-the-web) geschrieben.

**Die Tastatur springt auf, obwohl du sie nicht wolltest.** Auf dem Desktop fokussiert das Popover das Suchfeld, damit du sofort tippen kannst. Auf einem Touchscreen fokussiert es stattdessen das Panel, du siehst beim Öffnen also das Raster ohne Tastatur; erst ein Tipp ins Suchfeld holt sie hoch. Unter 768px nutzt das Suchfeld 16px große Schrift, damit iOS die Seite nicht heranzoomt, und `inputMode="search"` gibt der Tastatur eine Suchtaste.

**Der Finger trifft die falsche Zelle.** Bei 36px trifft der Daumen die Nachbarzelle. Bei einem groben Zeiger wächst jede Zelle auf mindestens 44px, egal was `size` sagt, und die Zellen tragen `touch-manipulation`, damit ein Doppeltipp nicht zoomt.

Noch etwas: Eingabemethoden für Sprachen wie Japanisch setzen Text erst zusammen, bevor sie ihn übernehmen. Die Suche ignoriert Enter, solange `isComposing` true ist, damit das Bestätigen eines Worts nicht das erste Emoji auswählt.

## Wie funktionieren Hautfarben im Emoji Picker?

Unicode definiert fünf Modifikatoren für Hautfarben (Emoji Skin Tones), U+1F3FB bis U+1F3FF. Ein Modifikator steht direkt hinter dem Codepoint, der ihn annimmt: 👍 plus U+1F3FD ergibt 👍🏽. Emojis mit zwei Personen, etwa zwei Menschen, die sich an den Händen halten, bekommen hinter jeder Person einen Modifikator, und die meisten Emojis nehmen gar keinen an.

Die Datendatei speichert für jedes Emoji, wohin sein Modifikator gehört. So wendet der Picker eine einzige Wahl auf jede Hand und jede Person an und lässt 🍕 in Ruhe. Die Wahl wird unter `<storageKey>:skin-tone` gespeichert. Außerhalb des Pickers liefert `applySkinTone("👍", "medium")` ein 👍🏽, und `searchEmojis(query, { limit })` gibt dir dieselbe gewichtete Suche für eine `:shortcode`-Autovervollständigung in einem einfachen Textfeld.

## Gibt es einen React Emoji Picker für shadcn?

Nicht in shadcn/ui selbst: Stand 6. November 2026 führt die [Komponentenseite](https://ui.shadcn.com/docs/components) ein Popover, aber keinen Emoji Picker. Wer nach „react emoji picker shadcn“ sucht, landet deshalb bei fremden Registries: Frimousse und emoji-picker-react 5 veröffentlichen laut ihren READMEs beide einen Eintrag für die shadcn-Registry, und der Picker von Wingo UI wird genauso installiert (Quelldateien in `components/ui`, `cn()`, Design Tokens), nur über die eigene CLI. Wenn du im selben Nachrichtenfeld auch `@mentions` brauchst, behandelt [React Mentions](https://wingo-ui.com/de/blog/react-mentions) dieses Feld.

## Sollten Reaktionen auf Nachrichten den vollen Picker nutzen?

Nein. Für eine Reaktion auf eine Nachricht reicht eine Handvoll Optionen, keine 1.870. Der kostenlose [Emoji-Reaction-Button](https://wingo-ui.com/components/emoji-reaction) öffnet eine kurze Leiste mit fünf Apple-Emojis (standardmäßig Bilder von einem CDN, einstellbar über `emojiBaseUrl`) und lässt Kopien der Auswahl nach oben schweben. Drück und zieh direkt auf ein Emoji, um es in einer einzigen Geste auszuwählen:

> Live-Demo (Emoji Reaction): Drück den Button und zieh auf ein Emoji, oder öffne ihn mit einem Tipp und wähl eines aus. Probier sie unter [Emoji Reaction](https://wingo-ui.com/components/emoji-reaction) aus und installier sie mit `npx wingo-ui@latest add emoji-reaction`.

Der [Comment Thread](https://wingo-ui.com/components/comment-thread) geht genauso vor: Sein Prop `reactions` legt die Reihe der schnellen Reaktionen unter jedem Kommentar fest (standardmäßig 👍 ❤️ 😂 🎉 😮 🙏). Den vollen Picker behältst du für das Nachrichtenfeld oder hinter einem „Mehr“-Button neben den Reaktions-Chips.

## Was sind die typischen Stolperfallen bei Emoji Pickern?

Flaggen, die Länge von Strings, Hydration und alte Schriften verursachen die meisten Bug-Reports:

- **Flaggen unter Windows.** Segoe UI Emoji hat keine Glyphen für Landesflaggen, also erscheint 🇺🇸 als „US“, wie eine Moderatorenantwort auf [Microsoft Q&A](https://learn.microsoft.com/en-us/answers/questions/5554132/it-s-time-for-microsoft-to-fully-support-flag-emoj) bestätigt (geprüft am 6. November 2026). Setz `emojiFont` auf einen Stack, der mit einem Webfont mit Flaggen beginnt, wenn deine Nutzer sie brauchen, oder blende die Kategorie über `categories` aus.
- **Zeichen zählen.** `"👍🏽".length` ist 4. Für ein Zeichenlimit zählst du Grapheme mit `[...new Intl.Segmenter().segment(text)].length`.
- **Hydration.** Die zuletzt genutzten Emojis kommen aus `localStorage`, und das kann der Server nicht lesen. Der Picker rendert zuerst den Wert vom Server und liest den Speicher erst nach der Hydration, damit das Markup übereinstimmt; mach es genauso, wenn du `recent` selbst steuerst.
- **Alte Daten und alte Schriften.** Unsere Daten enden bei Emoji 15.0, Emojis aus späteren Unicode-Versionen fehlen also im Raster, und ein Gerät, dessen Schrift älter als diese Version ist, zeichnet manche Emojis aus dem Raster als leere Kästchen.

Weitere Guides dieser Art findest du in der [Kategorie Komponenten-Guides](https://wingo-ui.com/de/blog/category/components) und im Pillar-Artikel zu [React-Formularkomponenten](https://wingo-ui.com/de/blog/react-form-components-guide).

## Nächster Schritt

Öffne die [Doku zum Emoji Picker](https://wingo-ui.com/components/emoji-picker), um jeden Prop im Playground auszuprobieren, und installier ihn dann mit `npx wingo-ui@latest add emoji-picker`. Er gehört zu Wingo UI Pro ([Preise](https://wingo-ui.com/de/pricing)); Popover und Emoji Reaction sind mit einem kostenlosen Konto gratis.

## Komponenten in diesem Artikel

- [Emoji Picker](https://wingo-ui.com/components/emoji-picker) (Pro): A React emoji picker with search, category tabs, recent emoji, skin tones and a preview, bundling 1,870 emoji with names and keywords. Installation: `npx wingo-ui@latest add emoji-picker`
- [Emoji Reaction](https://wingo-ui.com/components/emoji-reaction) (Free): A React emoji reaction button in the chat tapback style: it opens a bar of Apple emoji and sends copies of your pick floating up. Installation: `npx wingo-ui@latest add emoji-reaction`
- [Popover](https://wingo-ui.com/components/popover) (Free): A React popover for rich content anchored to a trigger, like a quick filter, form or share panel, that becomes a bottom sheet on phones. Installation: `npx wingo-ui@latest add popover`
- [Comment Thread](https://wingo-ui.com/components/comment-thread) (Pro): A React comment thread for records: replies, reactions, edit and delete, @mentions and a composer that sends optimistically with retry. Installation: `npx wingo-ui@latest add comment-thread`

## Häufige Fragen

### Welche React-Emoji-Picker-Bibliothek ist die beste?

Das hängt davon ab, wie viel du selbst in der Hand haben willst. Stand 6. November 2026 ist emoji-picker-react (5.1.4) eine fertig gestylte Lösung mit Headless-Bausteinen, Frimousse (0.4.0) ist ungestylt und lädt seine Daten von einem CDN, und der Emoji Picker von Wingo UI wird als Quellcode installiert, mit mitgelieferten Daten, Design Tokens und einem Sheet fürs Smartphone.

### Hat shadcn/ui einen Emoji Picker?

Nein. Stand 6. November 2026 führt die Komponentenseite von shadcn/ui ein Popover, aber keinen Emoji Picker. Frimousse und emoji-picker-react veröffentlichen Einträge für die shadcn-Registry, und Wingo UI installiert seinen Picker als Quellcode über die eigene CLI.

### Wie funktionieren Hautfarben in einem Emoji Picker?

Eine Hautfarbe ist eines von fünf Unicode-Modifikatorzeichen (U+1F3FB bis U+1F3FF), das direkt hinter dem Codepoint steht, der es annimmt; so wird aus 👍 ein 👍🏽. Nur manche Emojis nehmen einen Modifikator an, also sollte ein Picker die gewählte Hautfarbe auf all diese anwenden, den Rest unverändert lassen und sich die Wahl merken.

### Warum erscheinen Flaggen-Emojis unter Windows als Buchstaben?

Segoe UI Emoji, die Emoji-Schrift von Windows, hat keine Glyphen für Landesflaggen, daher erscheint eine Flagge als ihr zweibuchstabiger Ländercode. Wenn deine Nutzer Flaggen brauchen, lass den Font-Stack des Pickers mit einem Webfont beginnen, der Flaggen enthält.

### Ist der Emoji Picker von Wingo UI kostenlos?

Nein, Emoji Picker und Comment Thread gehören zu Wingo UI Pro ($8 im Monat, $80 im Jahr oder einmalig $150). Das Popover, in dem er sich öffnet, und der Emoji-Reaction-Button sind kostenlos.

---

Quelle: https://wingo-ui.com/de/blog/react-emoji-picker
