dash-emoji-mart — emoji picker for Dash
An emoji picker for Plotly Dash 4.
emoji-mart — the picker behind Missive, and the one most React apps reach for — wrapped as a single Dash component.
Documentation · PyPI · Changelog · Discord
The goal is a component that is universal but specifically elegant on mobile: an emoji, icon and picture selector that is intuitive on any device.
Install
pip install dash-emoji-mart
Python 3.9+ and Dash 4.1+. The compiled JavaScript bundle ships inside the package — no
Node, no build step, no external_scripts. The full emoji data set is bundled too, so the
picker works offline and makes no requests of its own.
Iconify icon-set support is an optional extra:
pip install "dash-emoji-mart[iconify]"
Quick start
from dash import Dash, callback, html, Input, Output
from dash_emoji_mart import DashEmojiMart
app = Dash(__name__)
app.layout = html.Div([
DashEmojiMart(id="picker"),
html.Div(id="out", style={"fontSize": 48}),
])
@callback(Output("out", "children"), Input("picker", "value"))
def show(value):
return value or "Pick one"
if __name__ == "__main__":
app.run(debug=True)
Reading a selection
Every pick writes two props in one update, so a callback taking both fires once:
| Prop | Type | Contents |
|---|---|---|
value |
str |
The native glyph ("😀"), or the image URL for a custom emoji. |
selectedEmoji |
dict |
The whole emoji-mart object — id, name, native, unified, shortcodes, keywords, skin, and src for custom emojis. |
selectedEmoji is the cleanest way to tell a built-in emoji from a custom one, since only
custom emojis carry src:
@callback(Output("out", "children"), Input("picker", "selectedEmoji"))
def show(emoji):
if not emoji:
return "Nothing picked"
if emoji.get("src"):
return html.Img(src=emoji["src"], style={"height": 32})
return emoji["native"]
clickedOutside is an n_clicks-style counter that increments on each click outside the
picker — the signal for closing a popover.
Custom emojis
Categories of your own images, GIFs or SVGs sit alongside the built-in ones:
DashEmojiMart(
id="picker",
custom=[{
"id": "team",
"name": "Team",
"emojis": [{
"id": "party_parrot",
"name": "Party Parrot",
"keywords": ["dance", "party"],
"skins": [{"src": "https://example.com/parrot.gif"}],
"native": "",
"unified": "custom",
}],
}],
categoryIcons={"team": {"svg": '<svg width="1em" height="1em" ...></svg>'}},
categories=["frequent", "team", "people"], # place it in the order you want
)
Custom emojis have no native glyph, so value comes back as the image URL. Full details:
Custom emojis.
Iconify icon sets
Iconify hosts 150+ icon sets, several of them emoji
sets far larger than the one emoji-mart bundles — twemoji has ~4,000 glyphs, OpenMoji
~4,200. dash_emoji_mart.iconify reshapes any of them into pickable categories:
from dash_emoji_mart import DashEmojiMart
from dash_emoji_mart.iconify import iconify_to_emoji_mart
DashEmojiMart(
id="picker",
custom=iconify_to_emoji_mart("twemoji", max_icons_per_category=60),
)
Responses are cached on disk for 24 hours (DASH_EMOJI_MART_CACHE_DIR relocates it), and
a network failure degrades to an empty category list rather than an error. Full details:
Iconify icon sets.
Picker in a popover
Very few apps want a 400px picker sitting in the page. dmc.Popover gives you a trigger
that opens the picker and dismisses it on an outside click, both client-side, so the only
callback you write is the one that closes it after a pick:
@callback(
Output("popover", "opened"),
Input("picker", "value"),
prevent_initial_call=True,
)
def close_on_pick(_value):
return False
Do not also add a callback that toggles opened from the trigger's n_clicks:
dmc.Popover has already flipped opened itself by the time it runs, so reading it as
State and returning not opened closes the popover on the same click that opened it.
For a hand-rolled panel — no dmc.Popover — use the picker's clickedOutside counter
instead.
Full worked example: Picker in a popover.
Props
The complete, always-current table is at
emojimart.2plot.dev/api-reference, and in
the component docstring (help(DashEmojiMart)). The commonly used ones:
| Prop | Default | Choices |
|---|---|---|
perLine |
9 |
Emojis per row |
emojiSize |
24 |
px |
emojiButtonSize |
36 |
px |
emojiButtonRadius |
"100%" |
any CSS radius, e.g. "6px" |
theme |
"auto" |
auto, light, dark |
set |
"native" |
native, apple, facebook, google, twitter |
locale |
"en" |
en, ar, be, cs, de, es, fa, fi, fr, hi, it, ja, ko, nl, pl, pt, ru, sa, tr, uk, vi, zh |
categories |
[] (all) |
frequent, people, nature, foods, activity, places, objects, symbols, flags. Built-ins only — do not combine with custom, see below |
navPosition |
"top" |
top, bottom, none |
previewPosition |
"bottom" |
top, bottom, none |
previewEmoji |
"point_up" |
emoji id shown when nothing is hovered |
searchPosition |
"sticky" |
sticky, static, none |
skinTonePosition |
"preview" |
preview, search, none |
skin |
1 |
1–6 |
emojiVersion |
14 |
max Emoji version to show |
maxFrequentRows |
4 |
0 disables the frequent category |
exceptEmojis |
[] |
emoji ids to hide from the grid — search still finds them, see below |
noResultsEmoji |
"cry" |
shown when a search finds nothing |
noCountryFlags |
False |
hide country flags from the grid — search still finds them, see below |
icons |
"auto" |
auto, outline, solid |
autoFocus |
False |
focus the search input on mount |
dynamicWidth |
False |
fill the container instead of sizing from perLine |
emojiButtonColors |
[] |
hover backgrounds, cycled |
className / style |
— | applied to the wrapper element |
persistence / persisted_props / persistence_type |
— | standard Dash persistence |
set,localeandcustomare read once, when emoji-mart builds its internal store. Changing them on a mounted picker has no visible effect — wrap it in ahtml.Div(..., id=...)whose id varies with the value to force a remount (Dash keys each child on its id; akey=prop is not read for reconciliation). Every other prop updates in place.
exceptEmojisandnoCountryFlagsfilter the grid, not the search. Measured against emoji-mart 5.6.0: both remove the emoji from its category, whileSearchIndex.searchmatches over the unfiltered emoji map. WithnoCountryFlags=Truethe flags category shrinks to a small safe list and typing "united" still returns 🇬🇧 🇺🇸 🇦🇪 🇺🇳. Treat them as tidying, not as access control.Do not pass
categoriestogether withcustom. emoji-mart filterscategoriesagainst a snapshot it takes on the first picker initialised in the page's lifetime, so custom ids resolve on that first picker and are silently dropped on every one after it — making the result depend on which page a user opened first. Omitcategorieswhen usingcustom; your categories are then appended after the built-ins.
Development
git clone https://github.com/pip-install-python/dash-emoji-mart
cd dash_emoji_mart
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # docs site + the component, editable
# markdown2dash pins gunicorn<22, against the CVE-driven gunicorn>=23 floor in
# requirements.txt. pip cannot resolve both, so it installs without its
# dependency set — everything it actually needs is already in the line above.
pip install --no-deps markdown2dash==0.1.2
npm install
npm run build # webpack bundle + generated Python classes
python run.py # the docs site at http://127.0.0.1:8050
npm run build writes into dash_emoji_mart/ — both the bundle and the generated
DashEmojiMart.py. Both are committed: that is what lets pip install work without
Node, and what lets the Docker image build without it.
Before opening a PR:
python scripts/smoke_test.py # renders every docs page, checks every route
python scripts/check_release.py # version drift, stale bundle, packaging leaks
CI runs both against Dash 4.1.0 → 4.4.1 and installs the built wheel on Python 3.9 → 3.13. See CONTRIBUTING.md and RELEASING.md.
Documentation site
The docs at emojimart.2plot.dev are this repository's
docs/ directory: one folder per page, holding a markdown file and the example.py that
renders its live demo. pages/markdown.py walks them and registers each as a Dash page —
adding a page means adding a folder, with no Python wiring anywhere else.
Every page also serves /<page>/llms.txt with its prose and complete example source, for
pasting into a chat window.
Deployment is a Render Blueprint (render.yaml) building Dockerfile.
Upgrading from 0.0.x
0.2.0 raises the floor to Dash 4.1 / Python 3.9 and removes four props that no Dash app
could ever have used (onEmojiSelect, onClickOutside, onAddCustomEmoji,
getSpritesheetURL — all function-typed, and Dash cannot serialise a Python function).
value is unchanged, so callbacks reading it keep working. See the
changelog for the full list.
Credits
Built on emoji-mart by Missive. Icon sets from Iconify. Documentation shell from the dash-documentation-boilerplate.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dash_emoji_mart-0.2.1.tar.gz.
File metadata
- Download URL: dash_emoji_mart-0.2.1.tar.gz
- Upload date:
- Size: 222.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62fd4a96d843e67e6d857eff4caf1d55c527114f7a4be2760d1b99adfb14e3c7
|
|
| MD5 |
dc294f33d02698a1e78e742009d35199
|
|
| BLAKE2b-256 |
62f6edb4582d45d61d6ac5e4593a3426ebea8d3a95fcf2ab1acf9d12bffd528d
|
Provenance
The following attestation bundles were made for dash_emoji_mart-0.2.1.tar.gz:
Publisher:
release.yml on pip-install-python/dash-emoji-mart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dash_emoji_mart-0.2.1.tar.gz -
Subject digest:
62fd4a96d843e67e6d857eff4caf1d55c527114f7a4be2760d1b99adfb14e3c7 - Sigstore transparency entry: 2329027892
- Sigstore integration time:
-
Permalink:
pip-install-python/dash-emoji-mart@1e4961492b9bad35072ad2360507bd9d905ce571 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/pip-install-python
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1e4961492b9bad35072ad2360507bd9d905ce571 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dash_emoji_mart-0.2.1-py3-none-any.whl.
File metadata
- Download URL: dash_emoji_mart-0.2.1-py3-none-any.whl
- Upload date:
- Size: 209.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6581825600cf732aaac047d992135568ce1c39c75b9107e645db3fcf34f9a45
|
|
| MD5 |
649d15cee35dce2a8db33ab662169906
|
|
| BLAKE2b-256 |
2128fba7b9b3a4e9cc17496e54b1122593747c63059540848316bb7e5d3341da
|
Provenance
The following attestation bundles were made for dash_emoji_mart-0.2.1-py3-none-any.whl:
Publisher:
release.yml on pip-install-python/dash-emoji-mart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dash_emoji_mart-0.2.1-py3-none-any.whl -
Subject digest:
f6581825600cf732aaac047d992135568ce1c39c75b9107e645db3fcf34f9a45 - Sigstore transparency entry: 2329027950
- Sigstore integration time:
-
Permalink:
pip-install-python/dash-emoji-mart@1e4961492b9bad35072ad2360507bd9d905ce571 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/pip-install-python
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1e4961492b9bad35072ad2360507bd9d905ce571 -
Trigger Event:
push
-
Statement type: