Skip to article
Guides / Search

Search in your picker

Rank emoji by meaning as people type, in any picker, with results on every keystroke.

On this page

Search is free on every plan. People type what they mean, and the picker shows the right emoji on the same keystroke. This guide shows the two ways to add it: a ready picker, or your own UI on top of the SDK.

Use a ready picker

If you do not have a picker yet, start with one of these. They show every emoji by category, switch to the Emojisense ranking as soon as someone types, and handle the keyboard, skin tones and loading for you.

Search in your own picker

If you already have a picker, keep it and replace its search. Call the SDK on every keystroke and render the results. Dictionary results come back on the same keystroke. When the dictionary is unsure, meaning results arrive after a short pause and merge in.

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

export function EmojiResults({ query }: { query: string }) {
  const sense = useEmojisense({
    packBaseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
    shardsUrl: "https://api.emojisense.com/p/0.1.0", // optional, layer 2: free
    endpoint: "https://api.emojisense.com", // optional, layer 3: metered
    publishableKey: "pk_live_…",
  });
  const { results, status, layer } = useEmojiSearch(query, sense, { limit: 24 });

  return (
    <ul role="listbox" aria-label="Emoji" aria-busy={status === "loading"}>
      {results.map((result) => (
        <li key={result.id} role="option" aria-selected={false}>
          {result.emoji}
        </li>
      ))}
    </ul>
  );
}

The hosted API does not publish shards yet. With shardsUrl set, the SDK finds no shard index and asks the API, so the option is safe to keep. Each result is { emoji, id, score, source }. The id is the Emojibase hexcode of the base emoji, for example 1F996. The source is alias for the dictionary and semantic for meaning search. With custom emoji it can be custom, and with a culture file culture (see Culture layer).

States and layers

statusMeaning
idleThe query is empty.
aliasDictionary results only. The dictionary was confident, or no semantic layer had an answer.
loadingDictionary results are shown and a semantic request is on its way.
fusedMeaning results are merged in. layer says which layer answered: shard or api.
errorThe semantic request failed. The dictionary results stay on screen.

Never block the list on loading: the dictionary results are already good. Use it for a subtle hint at most.

Languages

Set a locale and the engine loads that language next to English and prefers it when ranking. People can still type English words.

TypeScript
// React: "es" loads the Spanish pack next to English.
const sense = useEmojisense({
  packBaseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
  locale: "es",
});

// Any framework:
const packs = await loadPacks({
  baseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
  locales: ["es"],
});
const engine = createEngine(packs);
engine.search("feliz cumpleaños", { locale: "es" });
  • “feliz cumpleaños” · es🥳 🎂 🎇
  • “joyeux anniversaire” · fr🥳 🎂 🎈
  • “生日快乐” · zh🎂 🎈 🎉
  • “kolay gelsin” · tr💪 👷 👷‍♂️

Load more aliases when idle

Each language has a core part and an extension part. The core part is enough for the first search. The extension part adds more aliases and typos. useEmojisense and the web component load it for you when the browser is idle. With the engine alone, do it yourself:

TypeScript
import { createEngine, loadPacks } from "emojisense";

const core = await loadPacks({ baseUrl: "https://api.emojisense.com/v1/pack/0.1.0" });
let engine = createEngine(core); // search works now

requestIdleCallback(async () => {
  const ext = await loadPacks({
    baseUrl: "https://api.emojisense.com/v1/pack/0.1.0",
    part: "ext",
  });
  engine = createEngine([...core, ...ext]); // more aliases and typos
});

For example, with the core part “ship it” gives 🚀 🚢 🛳️, and with the extension part 🚀 📦️ 🚢.

Labels and accessibility

Give each result an accessible name. The engine has the emoji’s name in every loaded language:

TypeScript
const entry = engine.get(result.id);
const label = entry?.labels[locale] ?? entry?.labels.en ?? result.emoji;

Render the results as an ARIA listbox with options, keep focus in the search input, and move the active option with aria-activedescendant. The ready pickers do all of this.

Skin tones

Results are base emoji. Apply the person’s tone when you show and insert them. The tones are none, light, medium-light, medium, medium-dark, dark.

TypeScript
import { applySkinTone } from "emojisense";

applySkinTone("👍", "medium"); // "👍🏽"
applySkinTone("🧑‍🤝‍🧑", "dark"); // "🧑🏿‍🤝‍🧑🏿"