DocsBrowse documentation

API Reference

This page is the package-level API reference for @slithy/frimousse.

It covers:

  • EmojiPicker components
  • hooks
  • additive supplemental APIs
  • supplemental item helpers
  • custom emoji helpers
  • frequency helpers
  • search configuration
  • styling hooks
  • exported types

Entry Points

Namespace export

import { EmojiPicker } from "@slithy/frimousse";

Available parts:

  • EmojiPicker.Root
  • EmojiPicker.Search
  • EmojiPicker.Viewport
  • EmojiPicker.List
  • EmojiPicker.Loading
  • EmojiPicker.Empty
  • EmojiPicker.SkinToneSelector
  • EmojiPicker.ActiveEmoji
  • EmojiPicker.ActiveItem
  • EmojiPicker.SkinTone

Preferred consumer-facing types

If you are building on the additive API, start from these public aliases rather than the lower-level internal type names:

  • Item
  • NativeItem
  • SupplementalItem
  • ItemSelection
  • Section
  • SupplementalConfig
  • SupplementalSearch

Those are the stable consumer-facing types used throughout the docs below.

Named exports

import {
  buildEmojiPickerFrequentSection,
  buildNativeSearchTermsMap,
  createCustomEmoji,
  createCustomSection,
  createSupplementalItem,
  createSupplementalSection,
  formatAsShortcode,
  getEmojiPrimaryShortcode,
  getEmojiShortcodes,
  getEmojiPickerUsageKey,
  isCustomEmoji,
  isNativeSelection,
  isSupplementalSelection,
  rankEmojiPickerUsage,
  recordEmojiPickerUsage,
  sanitizeEmojiPickerUsageEntries,
  useActiveEmoji,
  useActiveItem,
  useSkinTone,
} from "@slithy/frimousse";

Native Search Helpers

buildNativeSearchTermsMap(entries)

Builds a search.native.terms map from app-owned or companion-package metadata.

type NativeSearchTermSource = {
  emoji: string;
  shortcodes?: string[];
  aliases?: string[];
  terms?: string[];
};

Notes:

  • combines shortcodes, aliases, and terms
  • trims empty strings and removes blank entries
  • deduplicates repeated values per emoji
  • returns the plain object shape expected by search.native.terms

formatAsShortcode(value)

Formats a display label into a normalized shortcode-like token.

Notes:

  • returns colon-wrapped values like :party_parrot:
  • useful when your app persists shortcode-first identifiers
  • intentionally small in scope; it does not provide a full shortcode dataset

getEmojiShortcodes(source, options?)

Returns normalized shortcode-like display tokens for an item or widened item selection.

Notes:

  • supplemental items use shortcode when provided, otherwise fall back to id
  • supplemental aliases are included as alternate shortcode-like display forms
  • native items only return values when nativeShortcodes are provided by the consumer
  • use this for display-oriented shortcode output, not for defining item identity

getEmojiPrimaryShortcode(source, options?)

Returns the first normalized shortcode-like display token for an item or widened item selection.

Useful for footer or preview UIs that want one canonical :shortcode: string.

Components

EmojiPicker.Root

Base provider and interaction boundary for the picker tree.

Props:

Prop Type Default Notes
onEmojiSelect (emoji: Emoji) => void - Called when a native emoji is selected.
onItemSelect (selection: ItemSelection) => void - Called when any picker item is selected, including supplemental items. Additive; does not replace onEmojiSelect.
locale Locale "en" Emojibase locale.
skinTone SkinTone "none" Initial skin tone.
columns number 9 Number of visible list columns.
emojiVersion number browser-supported latest Restricts visible emoji by Emoji version.
emojibaseUrl string "https://cdn.jsdelivr.net/npm/emojibase-data" Base URL used to fetch Emojibase JSON datasets. Point this at your own hosted emojibase-data files when you want a self-hosted or offline-capable setup.
sticky boolean true Makes category headers sticky.
supplemental SupplementalConfig - Adds consumer-owned sections and supplemental search behavior.
search EmojiPickerSearchConfig - Adds native search enrichment data.
searchValue string - Controls the picker search value at the root level, including use cases that do not render EmojiPicker.Search.
onSearchValueChange (search: string) => void - Called whenever the picker search value changes. Useful with an external input or autocomplete field.

Built-in element props:

  • All built-in div props.

Attributes:

  • [frimousse-root]
  • [data-focused] when the picker or one of its descendants is focused

CSS variables:

  • --frimousse-emoji-font
  • --frimousse-viewport-width
  • --frimousse-viewport-height
  • --frimousse-row-height
  • --frimousse-category-header-height

Self-hosting with emojibaseUrl

@slithy/frimousse does not bundle the native emoji dataset into the package. By default it fetches emojibase-data JSON at runtime, but you can stage and host those files yourself.

Use the included staging helper:

pnpm exec frimousse-stage-emojibase-data --out ./public/emojibase-data

For multiple locales:

pnpm exec frimousse-stage-emojibase-data --out ./public/emojibase-data --locales en,fr,ja

Then point emojibaseUrl at that hosted path:

<EmojiPicker.Root locale="en" emojibaseUrl="/emojibase-data">
  <EmojiPicker.Search />
  <EmojiPicker.Viewport>
    <EmojiPicker.Loading>Loading…</EmojiPicker.Loading>
    <EmojiPicker.Empty>No emoji found.</EmojiPicker.Empty>
    <EmojiPicker.List />
  </EmojiPicker.Viewport>
</EmojiPicker.Root>

With that setup, the picker no longer depends on the public CDN. If your app can serve those files offline, the picker can work offline too.

EmojiPicker.Search

Search input for the picker.

Notes:

  • Can be used as an uncontrolled input.
  • Can also be controlled with value and onChange.
  • Is optional when EmojiPicker.Root is using searchValue and onSearchValueChange.
  • Prefer one control surface at a time: either control EmojiPicker.Search directly, or drive search from EmojiPicker.Root.

Built-in element props:

  • All built-in input props.

Attributes:

  • [frimousse-search]

EmojiPicker.Viewport

Scrollable viewport for loading, empty, and list content.

Built-in element props:

  • All built-in div props.

Attributes:

  • [frimousse-viewport]

EmojiPicker.List

Virtualized list renderer for categories, rows, and emoji buttons.

Props:

Prop Type Default Notes
components Partial<EmojiPickerListComponents> - Replaces internal renderers for category headers, rows, native emoji buttons, and supplemental emoji buttons.

Built-in element props:

  • All built-in div props.

Attributes:

  • [frimousse-list]

EmojiPickerListComponents shape:

  • CategoryHeader: ComponentType<EmojiPickerListCategoryHeaderProps>
  • Row: ComponentType<EmojiPickerListRowProps>
  • Emoji: ComponentType<EmojiPickerListEmojiProps>
  • SupplementalEmoji: ComponentType<EmojiPickerListSupplementalEmojiProps>

Inner components:

  • CategoryHeader
    • props: EmojiPickerListCategoryHeaderProps
    • attributes: [frimousse-category-header]
    • notes: all category headers should be the same size
    • shape:
      • inherits built-in div props except children
      • category: Category
  • Row
    • props: EmojiPickerListRowProps
    • attributes: [frimousse-row]
    • notes: all rows should be the same size
    • shape:
      • inherits built-in div props
  • Emoji
    • props: EmojiPickerListEmojiProps
    • attributes: [frimousse-emoji]
    • state: [data-active]
    • notes: all emoji buttons should be the same size
    • shape:
      • inherits built-in button props except children
      • emoji: Emoji & { isActive: boolean }
  • SupplementalEmoji
    • props: EmojiPickerListSupplementalEmojiProps
    • notes: supplemental button renderer for additive sections
    • shape:
      • inherits built-in button props except children
      • emoji: SupplementalItem & { isActive: boolean }

EmojiPicker.Loading

Rendered while emoji data is loading.

Built-in element props:

  • All built-in span props.

Attributes:

  • [frimousse-loading]

EmojiPicker.Empty

Rendered when the current search has no results.

Props:

Prop Type Notes
children ReactNode | ((props: EmojiPickerEmptyRenderProps) => ReactNode) Can be static content or a render callback.

Built-in element props:

  • All built-in span props except children.

Attributes:

  • [frimousse-empty]

EmojiPickerEmptyRenderProps:

  • search: string

EmojiPicker.SkinToneSelector

Built-in skin tone toggle button.

Props:

Prop Type Default Notes
emoji string "✋" Emoji used as the visual basis for skin tone variations.

Notes:

  • Cycles through available skin tones.
  • Use EmojiPicker.SkinTone or useSkinTone for a custom selector.

Built-in element props:

  • All built-in button props except children.

Attributes:

  • [frimousse-skin-tone-selector]

EmojiPicker.ActiveEmoji

Render-prop component for the currently active native emoji.

Props:

Prop Type Notes
children (props: EmojiPickerActiveEmojiRenderProps) => ReactNode Native-only active item surface.

EmojiPickerActiveEmojiRenderProps:

  • emoji?: Emoji

Prefer this when your UI is intentionally native-only.

EmojiPicker.ActiveItem

Render-prop component for the currently active item, including supplemental items.

Props:

Prop Type Notes
children (props: EmojiPickerActiveItemRenderProps) => ReactNode Widened active-item surface.

EmojiPickerActiveItemRenderProps:

  • item?: ItemSelection

Prefer this for any picker that may include supplemental items now or later.

EmojiPicker.SkinTone

Render-prop component for the current skin tone state.

Props:

Prop Type Default Notes
emoji string "✋" Emoji used to derive the variation list.
children (props: EmojiPickerSkinToneRenderProps) => ReactNode Render callback.

EmojiPickerSkinToneRenderProps:

  • skinTone: SkinTone
  • setSkinTone: (skinTone: SkinTone) => void
  • skinToneVariations: SkinToneVariation[]

Hooks

useActiveEmoji()

Returns the currently active native emoji, or undefined.

const activeEmoji = useActiveEmoji();

Return type:

  • Emoji | undefined

Notes:

  • Native-only.
  • Returns the hovered item or the keyboard-active item.
  • Prefer useActiveItem() if your picker may include supplemental items.
  • If you later add supplemental items, switch alongside onItemSelect and EmojiPicker.ActiveItem.

useActiveItem()

Returns the currently active item, including supplemental items.

const activeItem = useActiveItem();

Return type:

  • ItemSelection | undefined

Notes:

  • Widened additive active-item surface.
  • Prefer this when your picker may include supplemental items now or later.
  • Pair this with onItemSelect and EmojiPicker.ActiveItem for a consistent mixed-item surface.

useSkinTone(emoji = "✋")

Returns skin tone state, setter, and skin tone variations for the provided emoji.

const [skinTone, setSkinTone, skinToneVariations] = useSkinTone();

Return type:

  • [SkinTone, (skinTone: SkinTone) => void, SkinToneVariation[]]

Additive Supplemental API

The additive contract is centered on a few concepts:

  • SupplementalItem: a consumer-owned non-native picker item
  • Section: a consumer-owned section that can contain native items, supplemental items, or both
  • ItemSelection: a discriminated selection union with kind: "native" or kind: "supplemental"
  • SupplementalConfig: the EmojiPicker.Root prop that wires additive sections and supplemental search behavior into the picker

supplemental root prop

EmojiPickerRootProps["supplemental"] accepts:

type SupplementalConfig = {
  sections?: Section[];
  search?: SupplementalSearch;
};

Use SupplementalConfig, Section, Item, and ItemSelection as the consumer-facing additive types.

SupplementalSearch:

Prop Type Default Notes
mode "grouped" | "unified" "grouped" Grouped mode preserves native category headings and matching supplemental section labels; unified mode merges everything into one results section.
resultsLabel string - Label used for unified result sections only. Ignored in grouped mode.
weights SupplementalSearchWeights built-in relevance defaults Overrides how strongly label, aliases, keywords, tags, and id contribute to supplemental match ranking. Applies to supplemental items only.

Grouped-mode guarantees:

  • native matches stay under native category headings
  • matching supplemental sections keep their own labels
  • sections with no matches are omitted
  • resultsLabel is ignored

Unified-mode guarantees:

  • native and supplemental matches merge into one results section
  • resultsLabel names that merged section
  • section labels no longer partition the rendered result set

Items, sections, and item selections

type NativeItem = {
  kind: "native";
  id: string;
  emoji: string;
  label: string;
};

type SupplementalItem = {
  kind: "supplemental";
  id: string;
  label: string;
  imageUrl?: string;
  tags?: string[];
  keywords?: string[];
  aliases?: string[];
  data?: unknown;
};

type Item =
  | NativeItem
  | SupplementalItem;

type Section = {
  id: string;
  label?: string;
  position?: "prepend" | "append";
  items: Item[];
  searchable?: boolean;
};

type NativeItemSelection = {
  kind: "native";
  item: NativeItem;
};

type SupplementalItemSelection = {
  kind: "supplemental";
  item: SupplementalItem;
};

type ItemSelection =
  | NativeItemSelection
  | SupplementalItemSelection;

searchable behavior:

  • true or omitted: the section participates in search
  • false: the section still renders when search is empty, but contributes no matches once search is active

Selection guards

isNativeSelection(selection)
isSupplementalSelection(selection)

Use these to narrow ItemSelection unions safely.

Supplemental Item Helpers

createSupplementalItem(input)

Creates a normalized supplemental item.

Input type:

type SupplementalItemInput = {
  id: string;
  label?: string;
  shortcode?: string;
  imageUrl?: string;
  tags?: string[];
  keywords?: string[];
  aliases?: string[];
  data?: unknown;
};

Field semantics:

  • id: required stable identity owned by the consumer
  • label: display label; defaults to id when omitted
  • shortcode: optional canonical display-oriented :shortcode: token
  • imageUrl: optional presentation data for image-backed rendering
  • aliases: alternate names that participate in supplemental search
  • keywords: search-oriented descriptors that participate in supplemental search
  • tags: broader search hints that also participate in supplemental search
  • data: opaque consumer payload carried through unchanged and not interpreted by the picker

Recommended mental model:

  • id answers “which item is this?”
  • shortcode answers “which :shortcode: should I display for this item?”
  • aliases, keywords, and tags answer “how should this item be found?”

Common patterns:

  1. Shortcode-first custom emoji
{
  id: "shipit",
  label: "Ship It",
  shortcode: ":shipit:",
  aliases: ["ship-it", ":ship_it:"],
}

Use this when the shortcode-like name is the stable identity your app stores or exchanges.

  1. Plain supplemental item with search metadata
{
  id: "design-review",
  label: "Design review",
  imageUrl: "/emoji/design-review.png",
  keywords: ["review", "feedback"],
  tags: ["team"],
}

Use this when the item needs discoverability metadata but does not need a shortcode-oriented identity model.

Avoid treating these fields as interchangeable:

  • id is for identity, not just display
  • shortcode is for canonical display text, not required identity
  • aliases are alternate names, not arbitrary payload
  • keywords and tags are for discovery, not canonical naming
  • data is the escape hatch only for metadata the picker should carry but not interpret

Built-in supplemental search treats label and aliases as name-like matches, keeps keywords and tags as weaker discovery signals, and treats an exact canonical id match as a first-class name-like hit in unified shortcode-first search.

SupplementalSearchWeights:

type SupplementalSearchWeights = {
  label?: number;
  aliases?: number;
  keywords?: number;
  tags?: number;
  id?: number;
};

Use this only to tune supplemental item ranking. Native emoji ranking still comes from the native dataset plus search.native.terms, with an opinionated built-in ranking policy rather than a public weighting API.

Set a weight to 0 when you want that field ignored entirely.

Return type:

  • SupplementalItem

createSupplementalSection(items, options)

Creates a normalized supplemental section that can contain plain or image-backed supplemental items.

Arguments:

  • items: readonly SupplementalItemInput[]
  • options: SupplementalSectionOptions

SupplementalSectionOptions:

type SupplementalSectionOptions = {
  id: string;
  label?: string;
  position?: "prepend" | "append";
  searchable?: boolean;
};

Return type:

  • SupplementalSection

Custom Emoji Helpers

createCustomEmoji(input)

Creates a normalized image-backed supplemental item with required id and imageUrl.

Input type:

type CustomEmojiInput = {
  id: string;
  label?: string;
  imageUrl: string;
  tags?: string[];
  keywords?: string[];
  aliases?: string[];
  data?: unknown;
};

CustomEmojiInput keeps the same metadata semantics as SupplementalItemInput, but requires imageUrl.

Return type:

  • CustomEmoji

createCustomSection(items, options)

Creates a normalized section of image-backed custom emoji.

Arguments:

  • items: readonly CustomEmojiInput[]
  • options: CustomSectionOptions

CustomSectionOptions:

type CustomSectionOptions = {
  id: string;
  label?: string;
  position?: "prepend" | "append";
  searchable?: boolean;
};

Return type:

  • CustomSection

isCustomEmoji(item)

Type guard for a supplemental item with a required imageUrl.

Frequency Helpers

Types

type EmojiPickerUsageSource = Item | ItemSelection;

type EmojiPickerUsageEntry = {
  key: string;
  item: Item;
  score: number;
  uses: number;
  lastUsedAt: number;
};

type EmojiPickerUsageOptions = {
  mode?: "frecency" | "recent";
  halfLifeMs?: number;
  maxEntries?: number;
  now?: Date | number;
};

type EmojiPickerFrequentSectionOptions = EmojiPickerUsageOptions & {
  id?: string;
  label?: string;
  limit?: number;
  position?: "prepend" | "append";
  searchable?: boolean;
};

getEmojiPickerUsageKey(source)

Returns a stable usage key in the form ${kind}:${id}.

rankEmojiPickerUsage(entries, options?)

Decays and sorts usage entries.

Behavior:

  • uses mode: "frecency" by default
  • decays each entry’s score using halfLifeMs
  • drops entries whose decayed score reaches 0
  • sorts by decayed score, then lastUsedAt, then uses
  • defaults halfLifeMs to 30 days and maxEntries to 100
  • in recent mode, sorts by lastUsedAt, then uses, then score

Return type:

  • EmojiPickerUsageEntry[]

recordEmojiPickerUsage(entries, source, options?)

Adds or updates a usage entry and returns the re-ranked collection.

Behavior:

  • resolves the stable key as ${kind}:${id}
  • in frecency mode, decays any existing score before adding the new usage
  • increments uses by 1
  • updates lastUsedAt to now
  • inserts a new entry with score: 1 when the item has not been seen before
  • in recent mode, keeps the same stored score behavior but ranks by recency instead

Return type:

  • EmojiPickerUsageEntry[]

sanitizeEmojiPickerUsageEntries(value)

Sanitizes persisted usage-entry data from consumer-owned storage.

Behavior:

  • accepts unknown input and returns a valid EmojiPickerUsageEntry[]
  • drops malformed entries instead of throwing
  • drops entries with non-finite or non-positive score and uses values
  • drops entries with non-finite or negative lastUsedAt values
  • deduplicates entries by sanitized usage key
  • re-derives each entry key from the sanitized item shape
  • normalizes supplemental items with the same rules as createSupplementalItem

Typical use:

function readEmojiPickerUsageEntries() {
  if (typeof window === "undefined") {
    return [];
  }

  try {
    const raw = window.localStorage.getItem("emoji-picker-usage");

    return sanitizeEmojiPickerUsageEntries(raw ? JSON.parse(raw) : []);
  } catch {
    return [];
  }
}

Return type:

  • EmojiPickerUsageEntry[]

buildEmojiPickerFrequentSection(entries, options?)

Builds a frequent-items section from ranked usage.

This uses the same frecency ranking as rankEmojiPickerUsage, then takes the top limit items and turns them into a supplemental section.

Use mode: "recent" when you want a “Recently used” section instead.

Return type:

  • EmojiPickerFrequentSection | null

Defaults:

  • section id: "frequently-used"
  • section label: "Frequently used"
  • section position: "prepend"
  • section searchable: false
  • section limit: 20

Search Configuration

EmojiPickerRootProps["search"] accepts:

type EmojiPickerNativeSearchConfig = {
  terms?: Record<string, string[]>;
};

type EmojiPickerSearchConfig = {
  native?: EmojiPickerNativeSearchConfig;
};

Use this to enrich native emoji matching with consumer-owned search terms.

Styling Hooks

Stable styling hooks exposed on built-in parts:

  • [frimousse-root]
  • [frimousse-search]
  • [frimousse-viewport]
  • [frimousse-list]
  • [frimousse-category-header]
  • [frimousse-row]
  • [frimousse-emoji]
  • [frimousse-loading]
  • [frimousse-empty]
  • [frimousse-skin-tone-selector]

Relevant state hook:

  • [data-active] on emoji buttons

The implementation also uses internal measurement and sizing attributes. Treat those as implementation details unless documented elsewhere.

Exported Types

This section lists the types re-exported from the package entrypoint.

Core

  • Category
  • Emoji
  • Locale
  • SkinTone

Items and selections

  • Item
  • NativeItem
  • SupplementalItem
  • ItemSelection
  • NativeItemSelection
  • SupplementalItemSelection
  • Section
  • SupplementalConfig
  • SectionPosition
  • SupplementalSearch
  • SupplementalSearchWeights
  • SearchConfig
  • EmojiPickerNativeSearchConfig
  • EmojiPickerSearchConfig
  • NativeSearchTermSource

Component props and render props

  • EmojiPickerRootProps
  • EmojiPickerSearchProps
  • EmojiPickerViewportProps
  • EmojiPickerListProps
  • EmojiPickerLoadingProps
  • EmojiPickerEmptyProps
  • EmojiPickerSkinToneSelectorProps
  • EmojiPickerActiveEmojiProps
  • EmojiPickerActiveEmojiRenderProps
  • EmojiPickerActiveItemProps
  • EmojiPickerActiveItemRenderProps
  • EmojiPickerSkinToneProps
  • EmojiPickerSkinToneRenderProps
  • EmojiPickerListComponents
  • EmojiPickerListCategoryHeaderProps
  • EmojiPickerListRowProps
  • EmojiPickerListEmojiProps
  • EmojiPickerListSupplementalEmojiProps

Custom emoji and sections

  • SupplementalItemInput
  • SupplementalSection
  • SupplementalSectionOptions
  • CustomEmoji
  • CustomEmojiInput
  • CustomSection
  • CustomSectionOptions

Frequency

  • EmojiPickerUsageEntry
  • EmojiPickerUsageMode
  • EmojiPickerUsageOptions
  • EmojiPickerUsageSource
  • EmojiPickerFrequentSection
  • EmojiPickerFrequentSectionOptions

Notes

  • If you later introduce supplemental items to a native-only picker, switch the three related surfaces together: onEmojiSelect -> onItemSelect, useActiveEmoji -> useActiveItem, and EmojiPicker.ActiveEmoji -> EmojiPicker.ActiveItem.
  • useActiveEmoji and EmojiPicker.ActiveEmoji remain native-only.
  • useActiveItem and EmojiPicker.ActiveItem include supplemental items.
  • onEmojiSelect remains native-only.
  • onItemSelect is the widened additive callback for both native and supplemental items.

Companion Package API Reference

The sections below cover optional companion packages. They are documented here for convenience, but they are separate installs and separate entry points.

@slithy/emoji-transforms

Install:

pnpm add @slithy/emoji-transforms

Purpose:

  • transforms consumer-owned emoji metadata into plain data structures for shortcode lookup and native search enrichment

Boundary:

  • this package does not render anything
  • this package does not bundle shortcode datasets
  • this package does not expose picker-specific UI contracts

Entry point:

import {
  adaptNativeEmojiSearchEntries,
  adaptEmojibaseNativeEmojiSearchEntries,
  buildFallbackTermsFromEmojibase,
  buildLabelMap,
  buildLabelMapFromAdapter,
  buildLabelMapFromEmojibase,
  mergeNativeEmojiLabelMaps,
  buildNativeEmojiSearchTermMap,
  buildNativeEmojiSearchTermMapFromAdapter,
  buildNativeEmojiSearchTermMapFromEmojibase,
  mergeNativeEmojiSearchTermMaps,
  buildShortcodeMap,
  buildShortcodeMapFromAdapter,
  buildShortcodeMapFromEmojibase,
  buildShortcodeMapFromPreset,
  getLabel,
  getNativeEmojiSearchTerms,
  getPrimaryShortcode,
  getShortcodes,
  normalizeNativeEmojiSearchKey,
} from "@slithy/emoji-transforms";

Search term, label, and shortcode builders

buildLabelMap(entries)

Builds a plain Record<string, string> keyed by normalized base emoji.

Input shape:

type NativeEmojiLabelEntry = {
  emoji: string;
  label: string;
};

Notes:

  • trims label values
  • normalizes keys to the base emoji form
  • keeps the first non-empty label for each normalized emoji key
  • useful when your app wants localized native emoji labels without keeping raw datasets in memory

buildNativeEmojiSearchTermMap(entries)

Builds a plain Record<string, string[]> keyed by normalized base emoji.

Input shape:

type NativeEmojiShortcodeEntry = {
  emoji: string;
  shortcodes?: readonly string[];
  aliases?: readonly string[];
};

Notes:

  • combines shortcodes and aliases
  • trims, lowercases, and deduplicates values
  • adds spaced variants for separator-based terms
  • returns the plain shape expected by @slithy/frimousse search.native.terms

buildShortcodeMap(entries)

Builds a shortcode-only Record<string, string[]> keyed by normalized base emoji.

Notes:

  • stays shortcode-only
  • does not widen into label or tag aliases
  • useful when your app wants a canonical native shortcode display map

buildNativeEmojiSearchTermMapFromAdapter(entries, adapter)

Combines an adapter step with native-search map construction.

buildLabelMapFromAdapter(entries, adapter)

Combines an adapter step with label-map construction.

buildShortcodeMapFromAdapter(entries, adapter)

Combines an adapter step with shortcode-map construction.

adaptNativeEmojiSearchEntries(entries, adapter)

Maps arbitrary source records into the package’s neutral NativeEmojiShortcodeEntry format.

adaptEmojibaseNativeEmojiSearchEntries(entries, options?)

Adapts a common Emojibase-like record shape into the package’s neutral entry format.

buildFallbackTermsFromEmojibase(entries, options?)

Builds a native-search term map from a secondary Emojibase locale dataset for fallback search use cases.

Notes:

  • includes label-based fallback terms by default
  • keeps tags opt-in through options
  • useful when a picker renders in one locale but should still match English search terms

Build-time example:

// scripts/build-fr-search-terms.ts
import { writeFile } from "node:fs/promises";
import englishData from "emojibase-data/en/data.json";
import frenchData from "emojibase-data/fr/data.json";
import {
  buildFallbackTermsFromEmojibase,
  buildNativeEmojiSearchTermMapFromEmojibase,
  mergeNativeEmojiSearchTermMaps,
} from "@slithy/emoji-transforms";

const searchTerms = mergeNativeEmojiSearchTermMaps(
  buildNativeEmojiSearchTermMapFromEmojibase(frenchData, {
    includeLabel: true,
  }),
  buildFallbackTermsFromEmojibase(englishData),
);

await writeFile(
  new URL("../src/generated/fr-search-terms.json", import.meta.url),
  `${JSON.stringify(searchTerms, null, 2)}\n`,
);

Run example:

pnpm tsx scripts/build-fr-search-terms.ts

That writes src/generated/fr-search-terms.json, which you can then pass to the picker:

import frSearchTerms from "./generated/fr-search-terms.json";

<EmojiPicker.Root
  locale="fr"
  search={{ native: { terms: frSearchTerms } }}
>
  {/* ... */}
</EmojiPicker.Root>;

buildNativeEmojiSearchTermMapFromEmojibase(entries, options?)

Builds a native-search term map directly from Emojibase-like records.

Notes:

  • shortcode data is included by default
  • label and tags are opt-in through options

buildLabelMapFromEmojibase(entries)

Builds a label-only map directly from Emojibase-like records.

Use this when your app wants localized emoji labels such as "❤️" -> "Red heart" without storing the full locale dataset at runtime.

mergeNativeEmojiLabelMaps(...maps)

Combines multiple plain native label maps into one normalized result.

Notes:

  • normalizes keys to the base emoji form
  • first non-empty label wins based on argument order
  • useful for multilanguage fallback chains such as fr -> en -> ja

mergeNativeEmojiSearchTermMaps(...maps)

Combines multiple plain native-search term maps into one normalized result.

Notes:

  • normalizes keys to the base emoji form
  • deduplicates terms across maps
  • useful for layering locale-fallback terms onto existing consumer-owned maps

buildShortcodeMapFromEmojibase(entries)

Builds a shortcode-only map directly from Emojibase-like records.

buildShortcodeMapFromPreset(entries, preset)

Joins Emojibase records with a hexcode-keyed shortcode preset such as emojibase-data/en/shortcodes/iamcal.json.

Use this when your app treats a preset like iamcal as the source of truth for native shortcode names.

Lookup helpers

getNativeEmojiSearchTerms(termMap, emoji)

Performs normalized lookup against a native-search term map.

getLabel(labelMap, emoji)

Performs normalized lookup against a native label map.

getShortcodes(shortcodeMap, emoji)

Performs normalized lookup against a native shortcode map.

getPrimaryShortcode(shortcodeMap, emoji)

Returns the first normalized shortcode for that emoji.

normalizeNativeEmojiSearchKey(emoji)

Exposes the base-key normalization used by the package.

Normalization behavior:

  • removes variation selectors
  • removes skin-tone modifiers
  • normalizes to a base-emoji lookup key

Exported types

  • NativeEmojiShortcodeEntry
  • NativeEmojiLabelEntry
  • NativeEmojiSearchAdapter<TEntry>
  • NativeEmojiLabelAdapter<TEntry>
  • EmojibaseNativeEmojiRecord
  • EmojibaseNativeEmojiSkinRecord
  • EmojibaseNativeEmojiSearchOptions
  • EmojibaseLocaleFallbackSearchOptions
  • EmojibaseShortcodePreset
  • NativeEmojiLabelMap
  • NativeEmojiSearchTermMap
  • NativeEmojiShortcodeMap

@slithy/emoji-compat

Install:

pnpm add @slithy/emoji-compat

Purpose:

  • detects native emoji support and builds fallback metadata for apps that need image fallbacks for newer emoji

Boundary:

  • this package does not render anything
  • this package does not bundle Twemoji assets
  • this package does not choose your app’s fallback policy

Entry point:

import {
  buildCompatMap,
  detectSupportedVersion,
  getCompatEntry,
  getFallbackUrl,
  isEmojiSupported,
  normalizeEmojiCompatKey,
} from "@slithy/emoji-compat";

Build-time asset helpers:

import {
  buildFallbackAssetManifest,
  collectFallbackHexcodes,
  downloadFallbackAssets,
} from "@slithy/emoji-compat/assets";

Runtime helpers

buildCompatMap(records, options?)

Builds fallback metadata keyed by native emoji strings.

Input shape:

type EmojiCompatRecord = {
  emoji: string;
  version: number;
  hexcode?: string;
  skins?: readonly {
    emoji: string;
    hexcode?: string;
  }[];
};

Notes:

  • if supportedVersion is provided, that version is treated as the native support floor
  • if omitted, the package detects support at runtime with its built-in probe

detectSupportedVersion(records, options?)

Determines the highest emoji version supported by the current browser across the provided records.

isEmojiSupported(emoji)

Low-level native-support probe for one emoji glyph.

getCompatEntry(compatMap, emoji)

Looks up one compat entry by normalized emoji key.

getFallbackUrl(compatMap, emoji, options?)

Returns a fallback asset URL when the emoji needs image fallback rendering.

Default convention:

  • /emoji/{hexcode}.svg

normalizeEmojiCompatKey(emoji)

Exposes the normalization used for compat-map storage and lookup.

Build-time asset helpers

buildFallbackAssetManifest(records, options)

Builds a stable manifest of fallback assets needed above a chosen versionFloor.

collectFallbackHexcodes(records, options)

Returns just the normalized lowercase hexcodes from that manifest.

downloadFallbackAssets(manifest, options)

Downloads matching fallback assets for a manifest.

Notes:

  • intended for build-time scripts
  • defaults to the jdecked/twemoji SVG source
  • does not run automatically at install or runtime

Exported types

  • CollectFallbackHexcodesOptions
  • DownloadFallbackAssetsOptions
  • DownloadFallbackAssetsResult
  • EmojiCompatRecord
  • EmojiCompatSkinRecord
  • EmojiCompatEntry
  • EmojiCompatMap
  • EmojiCompatOptions
  • EmojiFallbackUrlOptions
  • FallbackAssetKind
  • FallbackAssetRecord