1 Overview
Standard Unicode emoji are the same everywhere. Custom emoji are images that a user picks. This NIP lets an event carry its own emoji images. The author writes a shortcode like :soapbox: in the text. A tag maps the shortcode to an image URL.
The mechanism is one tag: emoji. Any client can read it. A client that supports this NIP replaces each :shortcode: with the image. A client that does not support it shows the plain :shortcode: text. Nothing breaks.
Custom emoji work in five event kinds: user metadata, text notes, reactions, comments, and user statuses. Section 4 lists them. An optional fourth tag element can point to the emoji set the emoji comes from.
Users of Slack, Discord, or the fediverse already know this pattern. The :shortcode: syntax is the same. The difference on Nostr: each event carries its own shortcode-to-image map, so no server has to host a shared emoji list.
2 The emoji Tag
Add one emoji tag per custom emoji that the event uses. An event may include one or more of these tags. The tag has this form:
["emoji", <shortcode>, <image-url>, <emoji-set-address>]
| Element | Required | Rule |
|---|---|---|
| shortcode | yes | The name of the emoji. It must contain only alphanumeric characters, hyphens, and underscores. |
| image-url | yes | A URL to the image file of the emoji. |
| emoji-set-address | no | An address pointer in the form kind:pubkey:d-tag. It points to the kind 30030 emoji set (NIP-51) that the emoji belongs to. |
In the text of the event, the author writes the shortcode between two colons: :shortcode:. The colons are markers only. They do not appear in the tag.
Validate shortcodes. A shortcode with spaces, colons, or other symbols is invalid. Only letters, digits, -, and _ are allowed. A loose parser can misread normal text such as timestamps (12:30:00) as emoji.
3 Emojification
To emojify means to replace shortcodes with images. For each emoji tag, a client should parse :shortcode: patterns in the event and display the custom emoji image in their place. Shortcodes without a matching tag stay as plain text. Regular Unicode emoji in the same text are untouched.
Clients may also help authors write custom emoji. A client may let the user insert a :shortcode: identifier in the event. The client then adds the matching emoji tags before it signs and publishes the event.
The optional fourth element links an emoji back to its source collection. An emoji set is an addressable kind 30030 event defined in NIP-51. The address lets a client offer "add this set" to the reader:
kind:pubkey:d-tag names the set the emoji belongs to.4 Supported Kinds
This NIP defines no new kind. It adds the emoji tag to five existing kinds. Each kind emojifies specific fields:
| Kind | Defined in | Emojified fields |
|---|---|---|
| 0 | NIP-01 user metadata | The name and about fields inside the JSON content. |
| 1 | NIP-10 text note | The content string. |
| 7 | NIP-25 reaction | The content string. A reaction can be one custom emoji. |
| 1111 | NIP-22 comment | The content string. |
| 30315 | NIP-38 user status | The status text in content. |
The source spec lists kind 30315 as supported but gives it no section and no example. The other four kinds each get one. Assume the same rule as kind 1: emojify the content string.
5 Worked Examples
The four events below come from the source spec. Each shows one supported kind. Long hex keys are shortened for print.
Kind 0 — metadata. The display name ends with :soapbox:. A supporting client renders the name with the image after the text:
{
"kind": 0,
"content": "{\"name\":\"Alex Gleason :soapbox:\"}",
"tags": [
["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"]
],
"pubkey": "79c2cae1…b820ff6",
"created_at": 1682790000
}
Kind 1 — text note. Three custom emoji mix with one Unicode emoji. The ablobcatrainbow tag carries the optional emoji-set address; the other two do not:
{
"kind": 1,
"content": "Hello :gleasonator: 😂 :ablobcatrainbow: :disputed: yolo",
"tags": [
["emoji", "ablobcatrainbow",
"https://gleasonator.com/emoji/blobcat/ablobcatrainbow.png",
"30030:79c2cae1…b820ff6:blobcats"],
["emoji", "disputed", "https://gleasonator.com/emoji/Fun/disputed.png"],
["emoji", "gleasonator", "https://gleasonator.com/emoji/Gleasonator/gleasonator.png"]
],
"pubkey": "79c2cae1…b820ff6",
"created_at": 1682630000
}
Kind 1111 — comment. A shortcode may contain underscores. The tag points to the "Hyves" emoji set:
{
"kind": 1111,
"content": "Let's party! :hyves_banaan:",
"tags": [
["emoji", "hyves_banaan",
"https://relay.nyves.nl/8408b236…bd2ebf.gif",
"30030:9919981d…a306b9:Hyves"]
],
"pubkey": "79c2cae1…b820ff6",
"created_at": 1682630000
}
Kind 7 — reaction. The whole content is a single shortcode. The reaction renders as one image:
{
"kind": 7,
"content": ":dezh:",
"tags": [
["emoji", "dezh", "https://raw.githubusercontent.com/dezh-tech/brand-assets/main/dezh/logo/black-normal.svg"]
],
"pubkey": "79c2cae1…b820ff6",
"created_at": 1682630000
}
6 Version History
The number 30 was used twice. A short "Event Copy" proposal held this slot in March 2023 and was dropped within a day. Alex Gleason then claimed the number for Custom Emoji. Notable changes, newest first:
- 20262026-08Added kind
1111comments (NIP-22) to the supported kinds. - 20262026-03Shortcode rule now allows hyphens. The optional emoji-set address became the 4th tag element.
- 20242024-09Added kind
7reactions (NIP-25): a reaction can be a custom emoji. - 20242024-01The supported-kinds list became explicit: 0, 1, 30315 at the time.
- 20232023Underscores allowed in shortcodes; author names removed from all NIP files.
- Origin2023-04Alex Gleason published "NIP-30: Custom Emoji", replacing the dropped Event Copy draft.
The manifest lists 2023-03-29 as the creation date. That commit belongs to the dropped Event Copy proposal, not to Custom Emoji. The first Custom Emoji commit is dated 2023-04-29. This page uses the later date.
Original Proposal - Credits
| Author | Alex Gleason |
| First commit | 2023-04-29 — “NIP-30: Custom Emoji” |
| Status | draft - optional — clients choose whether to render custom emoji |
| Source | 30.md on GitHub - full change history |
7 See Also
- NIP-01 — the event and tag structure that the
emojitag builds on; kind 0 metadata. - NIP-51 — kind
30030emoji sets, the collections the optional address points to. - NIP-25 — kind
7reactions, which can be a single custom emoji. - NIP-22 — kind
1111comments, emojified like text notes. - NIP-38 — kind
30315user statuses, emojified status text. - NIP-10 — kind
1text notes, the most common home for custom emoji.