12<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-30 - Custom Emoji — Nostr Instruction Manual</title>7<meta name="description" content="NIP-30: custom emoji for Nostr events. The emoji tag, shortcode rules, emojification, and the supported event kinds. Complete manual page.">8<link rel="preload" href="../assets/fonts/michroma-400-latin.woff2" as="font" type="font/woff2" crossorigin>9<link rel="preload" href="../assets/fonts/exo2-var-latin.woff2" as="font" type="font/woff2" crossorigin>10<link rel="preload" href="../assets/fonts/sairacondensed-600-latin.woff2" as="font" type="font/woff2" crossorigin>11<link rel="preload" href="../assets/fonts/sairacondensed-700-latin.woff2" as="font" type="font/woff2" crossorigin>12<link rel="preload" href="../assets/fonts/sairacondensed-500-latin.woff2" as="font" type="font/woff2" crossorigin>13<link rel="preload" href="../assets/fonts/sharetechmono-400-latin.woff2" as="font" type="font/woff2" crossorigin>14<link rel="preload" href="../assets/fonts/exo2-400i-latin.woff2" as="font" type="font/woff2" crossorigin>15<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">16<link rel="stylesheet" href="../assets/manual.css">17</head>18<body>1920<svg width="0" height="0" style="position:absolute">21 <defs>22 <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">23 <path d="M1,1 L8,4.5 L1,8 Z" fill="#f26a00"/>24 </marker>25 <marker id="arrb" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">26 <path d="M1,1 L8,4.5 L1,8 Z" fill="#2e5f9e"/>27 </marker>28 </defs>29</svg>3031<div class="page">32 <span class="staple s1"></span><span class="staple s2"></span>3334 <header class="band">35 <a class="logo" href="../index.html">NOSTR<b>▸</b>PROTOCOL MANUAL</a>36 <span class="booklet-note">Chapter 30 - Social</span>37 </header>38 <nav class="tabs">39 <a href="../index.html">Contents</a>40 <a href="#overview" class="on">Overview</a>41 <a href="#spec">Spec</a>42 <a href="#examples">Examples</a>43 <a href="#history">History</a>44 </nav>4546 <div class="chapter">47 <div class="nipno" data-n="30">30</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 30</div>50 <h1>Custom Emoji</h1>51 <p class="sub">Put your own emoji images into notes, names, reactions, and statuses with one simple tag and a <code>:shortcode:</code>.</p>52 </div>53 </div>5455 <main class="spread">56 <div class="main">5758 <h2 class="bar" id="overview"><span class="num">1</span> Overview</h2>59 <p>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 <strong>shortcode</strong> like <code>:soapbox:</code> in the text. A tag maps the shortcode to an image URL.</p>60 <p>The mechanism is one tag: <code>emoji</code>. Any client can read it. A client that supports this NIP replaces each <code>:shortcode:</code> with the image. A client that does not support it shows the plain <code>:shortcode:</code> text. Nothing breaks.</p>61 <p>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.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Users of Slack, Discord, or the fediverse already know this pattern. The <code>:shortcode:</code> 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.</p>66 </div>6768 <h2 class="bar" id="spec"><span class="num">2</span> The emoji Tag</h2>69 <p>Add one <code>emoji</code> tag per custom emoji that the event uses. An event may include one or more of these tags. The tag has this form:</p>7071 <div class="crt">72 <div class="titlebar">Wire format - emoji tag</div>73<pre>[<span class="s">"emoji"</span>, <span class="c"><shortcode></span>, <span class="c"><image-url></span>, <span class="c"><emoji-set-address></span>]</pre>74 </div>7576 <div class="tscroll"><table class="ref">77 <caption>Tag element reference</caption>78 <tr><th>Element</th><th>Required</th><th>Rule</th></tr>79 <tr><td class="btn">shortcode</td><td>yes</td><td>The name of the emoji. It must contain only alphanumeric characters, hyphens, and underscores.</td></tr>80 <tr><td class="btn">image-url</td><td>yes</td><td>A URL to the image file of the emoji.</td></tr>81 <tr><td class="btn">emoji-set-address</td><td>no</td><td>An address pointer in the form <code>kind:pubkey:d-tag</code>. It points to the kind <code>30030</code> emoji set (<a href="51.html">NIP-51</a>) that the emoji belongs to.</td></tr>82 </table></div>8384 <p>In the text of the event, the author writes the shortcode between two colons: <code>:shortcode:</code>. The colons are markers only. They do not appear in the tag.</p>8586 <div class="box warn">87 <span class="tag">Warning!</span>88 <p>Validate shortcodes. A shortcode with spaces, colons, or other symbols is invalid. Only letters, digits, <code>-</code>, and <code>_</code> are allowed. A loose parser can misread normal text such as timestamps (<code>12:30:00</code>) as emoji.</p>89 </div>9091 <h2 class="bar" id="emojify"><span class="num">3</span> Emojification</h2>92 <p>To <strong>emojify</strong> means to replace shortcodes with images. For each <code>emoji</code> tag, a client should parse <code>:shortcode:</code> 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.</p>93 <p>Clients may also help authors write custom emoji. A client may let the user insert a <code>:shortcode:</code> identifier in the event. The client then adds the matching <code>emoji</code> tags before it signs and publishes the event.</p>9495 <figure class="diagram">96 <div class="frame">97 <svg viewBox="0 0 560 200" role="img" aria-label="A client reads the event content and emoji tags, matches each shortcode to its image URL, and renders the text with inline images">98 <rect class="d-actor" x="10" y="20" width="150" height="50" rx="8"/>99 <text class="d-actor-label" x="85" y="42" text-anchor="middle">content</text>100 <text class="d-note" x="85" y="59" text-anchor="middle">"gm :soapbox:"</text>101 <rect class="d-actor" x="10" y="120" width="150" height="50" rx="8"/>102 <text class="d-actor-label" x="85" y="142" text-anchor="middle">emoji tags</text>103 <text class="d-note" x="85" y="159" text-anchor="middle">soapbox → https://…png</text>104 <rect class="d-actor" x="230" y="70" width="120" height="50" rx="8"/>105 <text class="d-actor-label" x="290" y="92" text-anchor="middle">Emojify</text>106 <text class="d-note" x="290" y="109" text-anchor="middle">match :name:</text>107 <rect class="d-actor" x="420" y="70" width="130" height="50" rx="8"/>108 <text class="d-actor-label" x="485" y="92" text-anchor="middle">Rendered note</text>109 <text class="d-note" x="485" y="109" text-anchor="middle">gm [img]</text>110 <path class="d-msg d-flow" d="M162,45 C200,50 205,70 227,82"/>111 <path class="d-msg d-flow" d="M162,145 C200,140 205,120 227,108"/>112 <path class="d-msg blue d-flow" d="M352,95 L417,95"/>113 <circle r="4" fill="#f26a00">114 <animateMotion dur="2.6s" repeatCount="indefinite" path="M162,45 C200,50 205,70 227,82"/>115 </circle>116 <circle r="4" fill="#2e5f9e">117 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M352,95 L417,95"/>118 </circle>119 </svg>120 </div>121 <figcaption>Fig. 30-1 — <b>The emojify pipeline.</b> The client joins the text with the tag map and renders each shortcode as an inline image.</figcaption>122 </figure>123124 <p>The optional fourth element links an emoji back to its source collection. An <strong>emoji set</strong> is an addressable kind <code>30030</code> event defined in <a href="51.html">NIP-51</a>. The address lets a client offer "add this set" to the reader:</p>125126 <figure class="diagram">127 <div class="frame">128 <svg viewBox="0 0 560 130" role="img" aria-label="The optional fourth element of the emoji tag is an address that resolves to a kind 30030 emoji set event">129 <rect class="d-actor" x="10" y="40" width="190" height="50" rx="8"/>130 <text class="d-actor-label" x="105" y="62" text-anchor="middle">emoji tag</text>131 <text class="d-note" x="105" y="79" text-anchor="middle">4th element: 30030:pubkey:blobcats</text>132 <rect class="d-actor" x="360" y="40" width="190" height="50" rx="8"/>133 <text class="d-actor-label" x="455" y="62" text-anchor="middle">kind 30030 event</text>134 <text class="d-note" x="455" y="79" text-anchor="middle">emoji set "blobcats" (NIP-51)</text>135 <path class="d-msg d-flow" d="M202,65 L357,65"/>136 <text class="d-label" x="280" y="55" text-anchor="middle">resolves to</text>137 </svg>138 </div>139 <figcaption>Fig. 30-2 — <b>Emoji set pointer.</b> The address <code>kind:pubkey:d-tag</code> names the set the emoji belongs to.</figcaption>140 </figure>141142 <h2 class="bar" id="kinds"><span class="num">4</span> Supported Kinds</h2>143 <p>This NIP defines no new kind. It adds the <code>emoji</code> tag to five existing kinds. Each kind emojifies specific fields:</p>144145 <div class="tscroll"><table class="ref">146 <caption>Kinds that support custom emoji</caption>147 <tr><th>Kind</th><th>Defined in</th><th>Emojified fields</th></tr>148 <tr><td class="btn">0</td><td><a href="01.html">NIP-01</a> user metadata</td><td>The <code>name</code> and <code>about</code> fields inside the JSON content.</td></tr>149 <tr><td class="btn">1</td><td><a href="10.html">NIP-10</a> text note</td><td>The <code>content</code> string.</td></tr>150 <tr><td class="btn">7</td><td><a href="25.html">NIP-25</a> reaction</td><td>The <code>content</code> string. A reaction can be one custom emoji.</td></tr>151 <tr><td class="btn">1111</td><td><a href="22.html">NIP-22</a> comment</td><td>The <code>content</code> string.</td></tr>152 <tr><td class="btn">30315</td><td><a href="38.html">NIP-38</a> user status</td><td>The status text in <code>content</code>.</td></tr>153 </table></div>154155 <div class="box tech">156 <span class="tag">Tech Note</span>157 <p>The source spec lists kind <code>30315</code> 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 <code>content</code> string.</p>158 </div>159160 <h2 class="bar" id="examples"><span class="num">5</span> Worked Examples</h2>161 <p>The four events below come from the source spec. Each shows one supported kind. Long hex keys are shortened for print.</p>162163 <p><strong>Kind 0 — metadata.</strong> The display name ends with <code>:soapbox:</code>. A supporting client renders the name with the image after the text:</p>164 <div class="crt">165 <div class="titlebar">Example - kind 0 with an emojified name</div>166<pre>{167 <span class="k">"kind"</span>: 0,168 <span class="k">"content"</span>: <span class="s">"{\"name\":\"Alex Gleason :soapbox:\"}"</span>,169 <span class="k">"tags"</span>: [170 [<span class="s">"emoji"</span>, <span class="s">"soapbox"</span>, <span class="s">"https://gleasonator.com/emoji/Gleasonator/soapbox.png"</span>]171 ],172 <span class="k">"pubkey"</span>: <span class="s">"79c2cae1…b820ff6"</span>,173 <span class="k">"created_at"</span>: 1682790000174}</pre>175 </div>176177 <p><strong>Kind 1 — text note.</strong> Three custom emoji mix with one Unicode emoji. The <code>ablobcatrainbow</code> tag carries the optional emoji-set address; the other two do not:</p>178 <div class="crt">179 <div class="titlebar">Example - kind 1 with three custom emoji</div>180<pre>{181 <span class="k">"kind"</span>: 1,182 <span class="k">"content"</span>: <span class="s">"Hello :gleasonator: 😂 :ablobcatrainbow: :disputed: yolo"</span>,183 <span class="k">"tags"</span>: [184 [<span class="s">"emoji"</span>, <span class="s">"ablobcatrainbow"</span>,185 <span class="s">"https://gleasonator.com/emoji/blobcat/ablobcatrainbow.png"</span>,186 <span class="s">"30030:79c2cae1…b820ff6:blobcats"</span>],187 [<span class="s">"emoji"</span>, <span class="s">"disputed"</span>, <span class="s">"https://gleasonator.com/emoji/Fun/disputed.png"</span>],188 [<span class="s">"emoji"</span>, <span class="s">"gleasonator"</span>, <span class="s">"https://gleasonator.com/emoji/Gleasonator/gleasonator.png"</span>]189 ],190 <span class="k">"pubkey"</span>: <span class="s">"79c2cae1…b820ff6"</span>,191 <span class="k">"created_at"</span>: 1682630000192}</pre>193 </div>194195 <p><strong>Kind 1111 — comment.</strong> A shortcode may contain underscores. The tag points to the "Hyves" emoji set:</p>196 <div class="crt">197 <div class="titlebar">Example - kind 1111 with an emoji-set address</div>198<pre>{199 <span class="k">"kind"</span>: 1111,200 <span class="k">"content"</span>: <span class="s">"Let's party! :hyves_banaan:"</span>,201 <span class="k">"tags"</span>: [202 [<span class="s">"emoji"</span>, <span class="s">"hyves_banaan"</span>,203 <span class="s">"https://relay.nyves.nl/8408b236…bd2ebf.gif"</span>,204 <span class="s">"30030:9919981d…a306b9:Hyves"</span>]205 ],206 <span class="k">"pubkey"</span>: <span class="s">"79c2cae1…b820ff6"</span>,207 <span class="k">"created_at"</span>: 1682630000208}</pre>209 </div>210211 <p><strong>Kind 7 — reaction.</strong> The whole <code>content</code> is a single shortcode. The reaction renders as one image:</p>212 <div class="crt">213 <div class="titlebar">Example - kind 7 reaction as one custom emoji</div>214<pre>{215 <span class="k">"kind"</span>: 7,216 <span class="k">"content"</span>: <span class="s">":dezh:"</span>,217 <span class="k">"tags"</span>: [218 [<span class="s">"emoji"</span>, <span class="s">"dezh"</span>, <span class="s">"https://raw.githubusercontent.com/dezh-tech/brand-assets/main/dezh/logo/black-normal.svg"</span>]219 ],220 <span class="k">"pubkey"</span>: <span class="s">"79c2cae1…b820ff6"</span>,221 <span class="k">"created_at"</span>: 1682630000222}</pre>223 </div>224225 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>226 <p>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:</p>227 <ul class="patchnotes">228 <li><span class="ver">2026</span><span class="when">2026-08</span>Added kind <code>1111</code> comments (<a href="22.html">NIP-22</a>) to the supported kinds.</li>229 <li><span class="ver">2026</span><span class="when">2026-03</span>Shortcode rule now allows hyphens. The optional emoji-set address became the 4th tag element.</li>230 <li><span class="ver">2024</span><span class="when">2024-09</span>Added kind <code>7</code> reactions (<a href="25.html">NIP-25</a>): a reaction can be a custom emoji.</li>231 <li><span class="ver">2024</span><span class="when">2024-01</span>The supported-kinds list became explicit: 0, 1, 30315 at the time.</li>232 <li class="minor"><span class="ver">2023</span><span class="when">2023</span>Underscores allowed in shortcodes; author names removed from all NIP files.</li>233 <li><span class="ver">Origin</span><span class="when">2023-04</span>Alex Gleason published "NIP-30: Custom Emoji", replacing the dropped Event Copy draft.</li>234 </ul>235236 <div class="box tech">237 <span class="tag">Tech Note</span>238 <p>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.</p>239 </div>240241 <div class="credits">242 <h3 class="cardhead">Original Proposal - Credits</h3>243 <table>244 <tr><td>Author</td><td>Alex Gleason</td></tr>245 <tr><td>First commit</td><td>2023-04-29 — “NIP-30: Custom Emoji”</td></tr>246 <tr><td>Status</td><td>draft - optional — clients choose whether to render custom emoji</td></tr>247 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/30.md">30.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/30.md">full change history</a></td></tr>248 </table>249 </div>250251 <h2 class="bar"><span class="num">7</span> See Also</h2>252 <ul>253 <li><a href="01.html">NIP-01</a> — the event and tag structure that the <code>emoji</code> tag builds on; kind 0 metadata.</li>254 <li><a href="51.html">NIP-51</a> — kind <code>30030</code> emoji sets, the collections the optional address points to.</li>255 <li><a href="25.html">NIP-25</a> — kind <code>7</code> reactions, which can be a single custom emoji.</li>256 <li><a href="22.html">NIP-22</a> — kind <code>1111</code> comments, emojified like text notes.</li>257 <li><a href="38.html">NIP-38</a> — kind <code>30315</code> user statuses, emojified status text.</li>258 <li><a href="10.html">NIP-10</a> — kind <code>1</code> text notes, the most common home for custom emoji.</li>259 </ul>260 </div>261262 <aside>263 <div class="cartridge">264 <div class="inner">265 <h3 class="cardhead">Spec Card</h3>266 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>267 <dl>268 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span></dd>269 <dt>Defines</dt><dd><code>emoji</code> tag - <code>:shortcode:</code> syntax</dd>270 <dt>Kinds</dt><dd>none new — applies to 0, 1, 7, 1111, 30315</dd>271 <dt>Requires</dt><dd><a href="01.html">NIP-01</a>; <a href="51.html">NIP-51</a> for emoji sets</dd>272 <dt>First commit</dt><dd>2023-04-29</dd>273 <dt>Last updated</dt><dd>2026-08-25</dd>274 </dl>275 </div>276 </div>277 </aside>278 </main>279280 <nav class="navbtns">281 <a href="29.html">◀ NIP-29: Relay-based Groups</a>282 <a href="31.html">NIP-31: Dealing with unknown event kinds ▶</a>283 </nav>284285 <footer class="pagefoot">286 <a href="../index.html">Nostr Protocol Manual</a>287 <span class="pageno">NIP 30 / F4</span>288 </footer>289</div>290291<script src="../assets/webmcp.js" defer></script>292</body>293</html>