Skip to article
Integrations / React + Frimousse

React and Frimousse

Hooks for any React picker, a drop-in Frimousse picker and a shadcn/ui component.

On this page

@emojisense/react has two hooks for any React picker, a drop-in picker built on Frimousse, and a shadcn/ui component. It works with React 18 and 19.

Install

npm install @emojisense/react frimousse

Frimousse is optional: leave it out if you only use the hooks.

Drop-in picker

EmojisensePicker keeps Frimousse’s browse view by category. While someone types, it shows the Emojisense ranking as an ARIA listbox, with the same onEmojiSelect contract and skin tone. It can mount before the packs arrive: it shows Frimousse’s loading state and remounts when they are ready.

EmojiPicker.tsx
import { useEmojisense } from "@emojisense/react";
import { EmojisensePicker } from "@emojisense/react/frimousse";

export function EmojiPicker({ onPick }: { onPick: (emoji: string) => void }) {
  const sense = useEmojisense({
    packBaseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
    endpoint: "https://api.emojisense.com",
    publishableKey: "pk_live_…",
  });

  return (
    <EmojisensePicker
      emojisense={sense}
      onEmojiSelect={({ emoji }) => onPick(emoji)}
      columns={9}
    />
  );
}
PropDefaultNotes
emojisense—Required. The value of useEmojisense.
onEmojiSelect—Required. Gets { emoji, label }, with the skin tone applied. A custom emoji has emoji :shortcode: and adds imageUrl and shortcode.
placeholder"Search emoji…"Search input placeholder
empty—Rendered when a query has no results
limit24Number of ranked results
columns9Emoji per row
showRelevantNowfalseShow a row of seasonal and event emoji from the culture file above the browse list. Needs cultureUrl.
relevantNowLabel"Relevant now"Heading of that row

Every other Frimousse root prop passes through. Style the picker like any Frimousse picker. The ranked results add two hooks:

CSS
/* Frimousse is unstyled. The ranked results add these hooks: */
[data-emojisense-results] { gap: 0.25rem; }
[data-emojisense-results] [role="option"] { border-radius: 0.5rem; font-size: 1.5rem; }
[data-emojisense-results] [data-active] { background: #f0f0f4; }

Hooks for your own picker

TSX
import { useEmojiSearch, useEmojisense } from "@emojisense/react";

const sense = useEmojisense({
  packBaseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
  shardsUrl: "https://api.emojisense.com/p/0.1.0",
  endpoint: "https://api.emojisense.com",
  publishableKey: "pk_live_…",
});
const { results, status, layer } = useEmojiSearch(query, sense, { limit: 24 });

useEmojisense(options)

Loads the packs once and builds the engine and the semantic layers.

OptionNotes
packBaseUrlRequired. The pack version directory. The core pack renders first.
localeDefault "en". Another locale, such as "es", loads that pack next to English.
shardsUrlPrecomputed results (layer 2), asked before the API. The hosted API does not publish shards yet; without a shard index the SDK asks the API.
endpointThe API (layer 3). Omit shardsUrl and endpoint for fully offline search.
publishableKeyYour pk_live_… key for the API.
extendedDefault true: load the extension packs when the browser is idle.
emojiSetHow the pickers draw emoji. "native" (default) uses the system font. "twemoji", "noto" and "fluent" draw images of a hosted set and need endpoint.
customEmojiDefault false. true loads the app’s custom emoji from GET /v1/custom-pack, so they are searched on the device and drawn as images. Needs endpoint and publishableKey.
tenantYour id for one of your customers: adds that tenant’s custom emoji.
cultureUrlThe culture files, for example https://api.emojisense.com/v1/culture/0.1.0. Emoji for the culture and the moment join the results after the top result, never above it (source: "culture"). Omit it for the canonical ranking.
regionAn ISO 3166-1 code such as "BR", for regional culture entries. Default: the region of the browser’s language, read on the device and never sent. "" = no region.

It returns { engine, semantic, packs, customPack, locale, status, extended, emojiSet, endpoint, culture, region, error }. status is loading, ready or error. To draw one emoji the same way as the pickers in your own components, use <EmojiGlyph emoji={emoji} emojiSet={sense.emojiSet} endpoint={sense.endpoint} />.

Searches as the query changes. Dictionary results update on every keystroke. Meaning results arrive after debounceMs (default 200) and merge in without moving confident hits. Options: limit (default 24), debounceMs and culture (false keeps the canonical ranking even when a culture file is loaded).

FieldMeaning
results{ emoji, id, score, source }[]. Custom emoji add imageUrl and shortcode; culture results add context and cultureId.
statusidle, alias, loading, fused or error
layerWhich layer produced the results: device, shard or api. undefined while idle or loading.
aliasThe on-device dictionary answer for the query, with its confidence, before fusion and culture
aliasMs, semanticMs, semanticCachedTimings, for latency displays

useRelevantNow(sense, options)

The emoji for an optional “relevant now” shelf: the featured seasonal and event entries of the culture file that are active on the device’s day, in the person’s region. It returns { emoji, hexcode, context, cultureId }[], empty without cultureUrl. Options: limit (default 8) and now.

shadcn/ui

The emoji-picker registry item is the Frimousse picker with Emojisense search, styled with Tailwind v4 theme tokens and your cn helper.

Terminal
npx shadcn@latest add https://<registry-host>/r/emoji-picker.json

The command copies components/ui/emoji-picker.tsx and installs @emojisense/react, emojisense and frimousse.

ReactionButton.tsx
import { useEmojisense } from "@emojisense/react";
import {
  EmojiPicker,
  EmojiPickerContent,
  EmojiPickerFooter,
  EmojiPickerSearch,
} from "@/components/ui/emoji-picker";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";

export function ReactionButton({ onPick }: { onPick: (emoji: string) => void }) {
  const sense = useEmojisense({ packBaseUrl, shardsUrl, endpoint });
  return (
    <Popover>
      <PopoverTrigger>😀</PopoverTrigger>
      <PopoverContent className="w-fit p-0">
        <EmojiPicker
          className="h-[21rem]"
          emojisense={sense}
          onEmojiSelect={({ emoji }) => onPick(emoji)}
        >
          <EmojiPickerSearch />
          <EmojiPickerContent />
          <EmojiPickerFooter />
        </EmojiPicker>
      </PopoverContent>
    </Popover>
  );
}
PartRole
EmojiPickerFrimousse root. Props: emojisense, onEmojiSelect, columns (9), limit (36) and the Frimousse root props.
EmojiPickerSearchCombobox input. Arrow keys move through the results, Enter selects.
EmojiPickerContentBrowse list for an empty query, ranked listbox for a typed query. empty sets the no-results text.
EmojiPickerFooterActive emoji preview and the skin tone selector

To build and host the registry yourself, run pnpm --filter @emojisense/react build and serve packages/react/dist/r at /r/ on any static host.