Vize

Emoji Picker

Accessible, unstyled, data-free emoji picker: consumers supply typed emoji data.

Package @vizejs/ui/emoji-picker
Maturity stable
Own the source vize lib pull emoji-picker
Requires context, controllable-state, id, listbox-grid
Aliases emoji picker, emoji selector, reaction picker
Covers WAI-ARIA grid pattern, emoji-mart, Frimousse, Ark UI

Usage

import { EmojiPicker, EmojiPickerRoot, EmojiPickerCategory, EmojiPickerEmpty, EmojiPickerGrid, EmojiPickerItem, EmojiPickerPreview, EmojiPickerSearch, EmojiPickerSkinTone } from "@vizejs/ui/emoji-picker";

Or copy the source into your project with vize lib pull emoji-picker (see Source Distribution).

API

EmojiPicker<T>

Source: src/families/selection/emoji-picker/emoji-picker-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned base id. null and undefined select a deterministic fallback.
items (required) readonly T[] required Every emoji item, in display order. No dataset ships with the picker.
getEmoji (required) (item: T) => string required Native glyph for the default tone.
getName (required) (item: T) => string required Human-readable name used for search, labels, and the preview.
getKeywords (item: T) => readonly string[] undefined Extra search keywords.
getCategory (item: T) => string undefined Category id of an item; omit for a single uncategorized section.
getSkins (item: T) => readonly string[] | undefined undefined Skin-tone variants indexed 0–5 (0 is the default tone).
categories readonly EmojiPickerCategory[] undefined Category order and labels; unknown categories follow in first-seen order.
columns number 8 Cells per row; must match the consumer's CSS grid.
skinTone EmojiSkinTone undefined Controlled skin tone. undefined selects uncontrolled behavior.
defaultSkinTone EmojiSkinTone 0 Initial skin tone for uncontrolled use.
search string undefined Controlled search text. undefined selects uncontrolled behavior.
filter EmojiPickerFilter<T> undefined Search filter. Defaults to an accent- and case-insensitive name/keyword match.
recent readonly T[] undefined Consumer-owned recently used items, shown as the first section.
recentLabel string "Recently used" Label of the recent section.
searchLabel string "Search results" Label of the search results section.
defaultLabel string "Emoji" Label of the single section used when getCategory is omitted.
dir "ltr" | "rtl" "ltr" Reading direction; rtl mirrors ArrowLeft and ArrowRight.

Events

Event Payload Description
select [item: T, glyph: string] Fired when an emoji is chosen, with the item and its glyph with the skin tone applied.
update:skinTone [tone: EmojiSkinTone] Fired when the skin tone requests a new controlled value.
update:search [text: string] Fired when the search text requests a new controlled value.

Slots

Slot Slot props Description
default EmojiPickerSlotState<T> Search, grid, categories, skin tones, and preview. Receives typed sections.

Exposed

Member Type Description
activeItem —
focusGrid —
setSearch —
skinTone —

EmojiPickerRoot<T>

Source: src/families/selection/emoji-picker/emoji-picker-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned base id. null and undefined select a deterministic fallback.
items (required) readonly T[] required Every emoji item, in display order. No dataset ships with the picker.
getEmoji (required) (item: T) => string required Native glyph for the default tone.
getName (required) (item: T) => string required Human-readable name used for search, labels, and the preview.
getKeywords (item: T) => readonly string[] undefined Extra search keywords.
getCategory (item: T) => string undefined Category id of an item; omit for a single uncategorized section.
getSkins (item: T) => readonly string[] | undefined undefined Skin-tone variants indexed 0–5 (0 is the default tone).
categories readonly EmojiPickerCategory[] undefined Category order and labels; unknown categories follow in first-seen order.
columns number 8 Cells per row; must match the consumer's CSS grid.
skinTone EmojiSkinTone undefined Controlled skin tone. undefined selects uncontrolled behavior.
defaultSkinTone EmojiSkinTone 0 Initial skin tone for uncontrolled use.
search string undefined Controlled search text. undefined selects uncontrolled behavior.
filter EmojiPickerFilter<T> undefined Search filter. Defaults to an accent- and case-insensitive name/keyword match.
recent readonly T[] undefined Consumer-owned recently used items, shown as the first section.
recentLabel string "Recently used" Label of the recent section.
searchLabel string "Search results" Label of the search results section.
defaultLabel string "Emoji" Label of the single section used when getCategory is omitted.
dir "ltr" | "rtl" "ltr" Reading direction; rtl mirrors ArrowLeft and ArrowRight.

Events

Event Payload Description
select [item: T, glyph: string] Fired when an emoji is chosen, with the item and its glyph with the skin tone applied.
update:skinTone [tone: EmojiSkinTone] Fired when the skin tone requests a new controlled value.
update:search [text: string] Fired when the search text requests a new controlled value.

Slots

Slot Slot props Description
default EmojiPickerSlotState<T> Search, grid, categories, skin tones, and preview. Receives typed sections.

Exposed

Member Type Description
activeItem T | undefined Highlighted item.
skinTone EmojiSkinTone Current skin tone.
focusGrid () => void Move DOM focus into the grid.
setSearch (text: string) => void Replace the search text.

EmojiPickerCategory<T>

Source: src/families/selection/emoji-picker/emoji-picker-category.vue

Props

Prop Type Default Description
section (required) EmojiPickerSection<T> required Section from the root slot state sections.

Slots

Slot Slot props Description
default EmojiPickerCellSlotState<T> Render one EmojiPickerItem per cell with :item and :index.
label { readonly label: string } Custom section label content.

EmojiPickerEmpty

Source: src/families/selection/emoji-picker/emoji-picker-empty.vue

Slots

Slot Slot props Description
default { readonly search: string } Message shown while no emoji is visible. Receives the search text.

EmojiPickerGrid

Source: src/families/selection/emoji-picker/emoji-picker-grid.vue

Props

Prop Type Default Description
ariaLabel string "Emoji" Accessible name of the grid.

Slots

Slot Slot props Description
default — One EmojiPickerCategory per section from the root slot state.

EmojiPickerItem<T>

Source: src/families/selection/emoji-picker/emoji-picker-item.vue

Props

Prop Type Default Description
item (required) T required Emoji item rendered by this cell.
index (required) number required Item index inside its section, from the category slot.

Slots

Slot Slot props Description
default EmojiPickerItemSlotState<T> Cell content; defaults to the glyph. Receives glyph, name, and highlight state.

EmojiPickerPreview

Source: src/families/selection/emoji-picker/emoji-picker-preview.vue

Slots

Slot Slot props Description
default EmojiPickerPreviewSlotState Preview of the highlighted emoji. Defaults to its glyph and name.

EmojiPickerSearch

Source: src/families/selection/emoji-picker/emoji-picker-search.vue

Props

Prop Type Default Description
placeholder string undefined Hint text shown while empty.
ariaLabel string "Search emoji" Accessible name of the search field.

EmojiPickerSkinTone

Source: src/families/selection/emoji-picker/emoji-picker-skin-tone.vue

Props

Prop Type Default Description
ariaLabel string "Skin tone" Accessible name of the radio group.
labels readonly string[] ["Default", "Light", "Medium-light", "Medium", "Medium-dark", "Dark"] Accessible names of tones 0–5.

Slots

Slot Slot props Description
default { readonly tone: EmojiSkinTone; readonly label: string; readonly checked: boolean; } Content of one tone swatch. Receives the tone, its label, and checked state.

Behavior

Contract

EmojiPicker is a headless, data-free emoji picker: no dataset ships, and the consumer maps typed items through required getEmoji/getName accessors plus optional getKeywords, getCategory, and getSkins. emoji-picker-root.vue is generic over the item type T, builds sections (recent first, then categories in order, then unknown categories; a non-empty search collapses into one "search" section), owns the controllable skinTone and search, and emits select(item, glyph) with the tone applied.

emoji-picker-grid.vue renders the APG role="grid" focus owner with aria-activedescendant. Navigation runs over the concatenated rows of every visible section: each section starts a new row and short rows are padded, so the listbox-grid moveInGrid helper moves in two dimensions and skips padding. emoji-picker-category.vue renders a labelled role="rowgroup" with a columnheader label row and role="row" chunks, emoji-picker-item.vue a role="gridcell", emoji-picker-search.vue a search field, emoji-picker-skin-tone.vue a roving radiogroup of six tones, emoji-picker-preview.vue a polite live preview, and emoji-picker-empty.vue the no-results state. No CSS ships.

Normative Behavior

# State Input Outcome Proven by
E1 any render focusable grid with aria-colcount/aria-rowcount (item plus label rows), labelled rowgroups, named gridcells renders an APG grid with labelled category rowgroups and gridcells
E2 grid focused arrows / Home / End / Ctrl+Home/End / PageUp/Down focus highlights the first cell; moves in 2-D across section boundaries, skipping padding cells 2-D keyboard navigation crosses section boundaries over padded rows
E3 highlighted cell Enter / Space / click emits select(item, glyph) with the skin tone applied; clicked cells become active Enter, Space, and click emit select with the skin tone applied
E4 skin tones arrows / Home / End / click roving tabindex radiogroup; arrows wrap and select; glyphs update; update:skinTone emits the skin-tone radiogroup uses roving focus and updates glyphs
E5 controlled tone click a tone emits and waits for the parent controlled skin tone waits for the parent
E6 search type / Enter / Escape / ArrowDown filters names and keywords accent-insensitively into one section, highlights the first match, Enter selects it, empty state shows, Escape clears, ArrowDown enters the grid search filters by name and keyword, highlights the first match, and drives the empty state
E7 recent render / navigate recent items form the first section; repeated items keep unique cell ids recent items form the first section and repeat safely
E8 pointer pointer move highlights the cell and the live preview shows glyph and name the preview follows the highlighted emoji
E9 no provider mount a part throws VIZE_UI_CONTEXT_MISSING parts require an EmojiPicker provider
E10 pure helpers sections / glyphs / virtual grid unknown categories follow in first-seen order; padding cells are empty; tones clamp to 0–5 pure helpers build sections, glyphs, and the padded virtual grid

SSR

Cell ids derive from the root id plus section and item positions, so server markup is byte-identical across isolated requests and hydrates without warnings (renders byte-identical EmojiPicker markup across isolated SSR requests, hydrates EmojiPicker without mismatches or node replacement). Large sets are filtered in O(n) per keystroke; rows are not virtualized.