YurbaEmojiPicker
Renders Noto Color Emoji (Google) from PNG files. Loads emoji data from a developer-supplied JSON with lazy pagination, dynamic category tabs, search, skin tone variant popups, and support for custom emojis.
<link rel="stylesheet" href="/dist/yurba-ep.min.css">
<script src="/dist/yurba-ep.min.js"></script>
The picker is created entirely from JavaScript via the static YurbaEP.create() factory — no HTML markup needed. With no arguments it loads the default Yurba CDN emoji set. It appends itself to document.body and returns the element.
const picker = YurbaEP.create()
picker.bind(
document.querySelector('#emoji-btn'),
document.querySelector('#message-input')
)
yurba-ep.select event always fires on the bound field. The picker also inserts the :emoji-code: shortcode at the caret of an <input> / <textarea>; for a contenteditable target it inserts the emoji <img> when insertImage is on, or leaves insertion to your event handler otherwise.
Live demo
<textarea> target:
<input> target:
contenteditable target:
API
All public methods available on the <yurba-ep> element instance.
const picker = YurbaEP.create(config)
Factory method — the only way to create a picker. Creates the <yurba-ep> element, appends it to document.body, and returns it.
| Config key | Type | Default | Description |
|---|---|---|---|
title optional |
string | 'Pick an emoji' |
Text shown in the picker header. |
emojiJson optional |
string | https://cdn.yurba.one/static/noto-emoji/emoji.json |
URL to the emoji JSON file. |
notoBase optional |
string | https://cdn.yurba.one/static/noto-emoji/png/72/ |
Base URL for Noto Color Emoji PNGs. |
groupHtml optional |
object | — | Category tab icon overrides, merged over the built-in defaults. Keys are emoji-metadata group names or 'all'. See Category icons. |
customEmojis optional |
array | — | Custom emoji categories appended after the standard ones. See Custom emojis. |
insertImage optional |
boolean | false |
Controls insertion into a contenteditable target. When true the picker inserts the emoji <img> (with alt=":code:" and data-emoji="code"). When false the picker inserts nothing into the editable — handle the yurba-ep.select event (which always fires) and render the emoji yourself. Has no effect on <input> / <textarea>, which always receive the :code: shortcode. |
Minimal — uses the default Yurba CDN emoji set:
const picker = YurbaEP.create()
picker.bind(emojiBtn, messageInput)
Full config:
const picker = YurbaEP.create({
title: 'Choose emoji',
emojiJson: 'https://cdn.yurba.one/static/noto-emoji/emoji.json',
notoBase: 'https://cdn.yurba.one/static/noto-emoji/png/72/',
insertImage: true,
groupHtml: {
'all': '<span class="material-symbols-rounded">more_horiz</span>',
'Flags': '<span class="material-symbols-rounded">flag</span>',
},
customEmojis: [
{
name: 'Yurba',
id: 'yurba',
html: '<span class="material-symbols-rounded">diamond</span>',
emojis: [
{
id: 'crystal',
keywords: ['crystal', 'diamond', 'yurba'],
src: 'https://cdn.yurba.one/static/emoji/yurba/png/crystal.png',
animated: false
}
]
}
],
})
.bind() on it immediately.picker.bind(button, input)
| Param | Type | Description |
|---|---|---|
button required |
HTMLElement | Trigger element. Clicking it opens the picker positioned relative to this element; clicking it again while open closes the picker. |
input required |
HTMLInputElement | HTMLTextAreaElement | contenteditable | Target field. On selection a yurba-ep.select event always fires on this element. The picker then inserts at the caret: for <input> / <textarea> the :code: shortcode (into .value); for a contenteditable element the emoji <img> when insertImage is on, otherwise nothing — leaving you to render it via the event. A native input event is dispatched after any built-in insertion. |
picker.bind(
document.querySelector('#emoji-btn'),
document.querySelector('#message-input')
)
picker.open()
Shows the picker. On the first call, fetches the emoji JSON and renders the initial tab. Subsequent calls skip the fetch. Uses the emojiJson passed to create() (defaults to the Yurba CDN set).
picker.close()
Hides the picker. Also closes any open skin tone variant popup. Triggered automatically on outside click, page scroll, or the ✕ button.
picker.selectTab(tabId)
| Param | Type | Description |
|---|---|---|
tabId required |
string | Tab ID to activate. Tab IDs are derived from JSON group names — spaces and special characters replaced with underscores, lowercased. Custom category IDs are also accepted. |
Built-in tab IDs, derived from the emoji-metadata group names: all, smileys_and_emotions, people, animals_and_nature, food_and_drink, travel_and_places, activities_and_events, objects, symbols, flags. Plus any custom category id.
Events
All events are dispatched as bubbling CustomEvents, namespaced under yurba-ep.*. Lifecycle events (open, close, load) fire on the <yurba-ep> element; yurba-ep.select fires on the bound field so you can intercept insertion.
| Event | Fires on | Description |
|---|---|---|
yurba-ep.select |
bound field | An emoji was chosen. Always fires, before any built-in insertion. |
yurba-ep.open |
<yurba-ep> |
The picker became visible. |
yurba-ep.close |
<yurba-ep> |
The picker was hidden (outside click, scroll, ✕, trigger re-click, or close()). |
yurba-ep.load |
<yurba-ep> |
The emoji JSON finished loading and the first tab rendered. |
Always fired on the bound field when an emoji is chosen, before any built-in insertion. The picker's own insertion is independent: <input> / <textarea> always get the :code: shortcode; a contenteditable target gets the emoji <img> only when insertImage is on — otherwise the picker leaves it untouched, so you render the emoji yourself in this handler.
event.detail | Type | Description |
|---|---|---|
code | string | Emoji code, e.g. grinning. |
shortcode | string | The code wrapped in colons, e.g. :grinning:. |
src | string | Image URL of the emoji (Noto PNG or custom src). |
animated | boolean | true for a custom emoji flagged animated; otherwise false. |
// insertImage = false → the picker won't touch the
// contenteditable, so you render the emoji however you like.
const editor = document.querySelector('#editor')
picker.bind(emojiBtn, editor)
editor.addEventListener('yurba-ep.select', (e) => {
const { code, shortcode, src } = e.detail
const img = document.createElement('img')
img.src = src
img.alt = shortcode
img.className = 'emoji'
editor.append(img)
})
Fired on the <yurba-ep> element when the picker becomes visible.
picker.addEventListener('yurba-ep.open', () => {
console.log('picker opened')
})
Fired on the <yurba-ep> element when the picker is hidden — by outside click, page scroll, the ✕ button, re-clicking the trigger, or a close() call.
picker.addEventListener('yurba-ep.close', () => {
console.log('picker closed')
})
Fired once on the <yurba-ep> element after the emoji JSON is fetched and the first tab is rendered.
event.detail | Type | Description |
|---|---|---|
count | number | Total number of loaded emojis (standard + custom). |
picker.addEventListener('yurba-ep.load', (e) => {
console.log(`${e.detail.count} emojis ready`)
})
Customization
Pass custom categories via the customEmojis config in create(). They are appended after the standard categories. Each category must have a name, an id, an html string for the tab icon, and an emojis array.
const picker = YurbaEP.create({
customEmojis: [
{
name: 'Yurba',
id: 'yurba',
html: '<span class="material-symbols-rounded">diamond</span>',
emojis: [
{
id: 'crystal',
keywords: ['crystal', 'diamond', 'yurba'],
src: 'https://cdn.yurba.one/static/emoji/yurba/png/crystal.png',
animated: false
},
...
]
}
]
})
| Field | Type | Description |
|---|---|---|
id required |
string | Emoji code. Inserted into the bound field as :id: on selection, and used as a search keyword. |
src required |
string | Image URL, rendered as an <img>. Any format (PNG, SVG, GIF, WebP). |
keywords optional |
string[] | Extra search keywords. The id is always searchable. |
animated optional |
boolean | Marks the emoji as animated (default false). Exposed as animated in the yurba-ep.select event detail so you can render it differently — the picker itself does not treat it specially. |
Category tab icons are keyed by the emoji-metadata group name. Each value is the raw HTML rendered inside the .y-ep__category tab button. Override them via the groupHtml config in create() — it is merged over the built-in defaults, so you only list the ones you want to change.
const picker = YurbaEP.create({
groupHtml: {
'all': '<span class="material-symbols-rounded">more_horiz</span>',
'Flags': '<img src="https://cdn.yurba.one/static/emoji/yurba/png/crystal.png" width="20" height="20">',
},
})
Available keys (match emoji-metadata group names exactly): all, Smileys and emotions, People, Animals and nature, Food and drink, Travel and places, Activities and events, Objects, Symbols, Flags.
<img>, <svg>, custom icon components, etc.
The picker natively supports the official Google emoji-metadata format — an array of groups with decimal Unicode codepoints. Download emoji_17_0_ordering.json from that repo and host it yourself.
[
{
"group": "Smileys and emotions",
"emoji": [
{
"base": [128075],
"alternates": [[128075], [128075, 127995], [128075, 127996],
[128075, 127997], [128075, 127998], [128075, 127999]],
"emoticons": [],
"shortcodes": [":wave:"],
"animated": false,
"directional": false
}
]
}
]
| Field | Description |
|---|---|
group |
Category name. Each group becomes its own tab. Tab ID is derived automatically: "Activities and events" → activities_and_events. Unknown groups get a generic icon. |
emoji[].base |
Decimal Unicode codepoints. Converted to hex for Noto filename: [128512] → emoji_u1f600.png. U+FE0F (65039) is stripped automatically. Codepoints below U+1000 are zero-padded to 4 digits: [35] → emoji_u0023.png. |
emoji[].alternates |
Skin tone (and gender) variant codepoint arrays. alternates[0] repeats base and is skipped. The remaining entries are shown in a popup when the emoji is clicked. |
emoji[].shortcodes |
Shortcode strings like ":wave:". The first one becomes the code appended to the input on selection (:wave:). Variants append a numeric suffix: :wave_1: … :wave_5:. |
emoji[].emoticons |
Text emoticons like ":D" — used as additional search keywords. |
The picker defines its own CSS variables on .y-ep with sensible defaults. Override them on the yurba-ep element or any parent to match your design — no dependency on external theme variables.
| Variable | Default | Description |
|---|---|---|
--y-ep-bg |
#ffffff |
Picker background and gradient fade color. |
--y-ep-secondary |
#f2f2f7 |
Secondary background — search bar, dividers, emoji hover, scrollbar. |
--y-ep-text |
#000000 |
Text and icon color. |
--y-ep-accent |
#007aff |
Accent color — active category tab, loader spinner. |
/* Dark theme example */
yurba-ep {
--y-ep-bg: #1c1c1e;
--y-ep-secondary: #2c2c2e;
--y-ep-text: #ffffff;
--y-ep-accent: #0a84ff;
}