clone

every nostr nip as a chapter of an illustrated, early-2000s printed game manual

main
nip.guide / site / nips / 25.html
25.html 21.2K
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-25 - Reactions — Nostr Instruction Manual</title>7<meta name="description" content="NIP-25: reactions. Kind 7 likes, dislikes, and emoji reactions, kind 17 external content reactions, and custom emoji shortcodes. 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 25 - 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="25">25</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 25</div>50 <h1>Reactions</h1>51 <p class="sub">One-tap responses to any event: likes, dislikes, and emoji. The smallest social signal in the protocol.</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>A <strong>reaction</strong> is a small event that points at another event and carries a short verdict. The verdict is a <code>+</code>, a <code>-</code>, or an emoji. Clients use reactions to build like buttons, upvote counters, and emoji rows.</p>60 <p>This NIP defines two event kinds. <strong>Kind 7</strong> reacts to a native Nostr event. <strong>Kind 17</strong> reacts to external content, such as a website or a podcast episode. Both kinds are ordinary signed events from <a href="01.html">NIP-01</a>. Any relay can store them. Any client can count them.</p>61 <p>A reaction does not modify its target. It only references the target with tags. To count likes for a note, a client subscribes to kind 7 events whose <code>e</code> tag matches the note's <code>id</code>.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Reactions are cheap to publish and cheap to query. One filter — <code>{"kinds":[7],"#e":[&lt;id&gt;]}</code> — returns every reaction to a note that the relay knows about.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 190" role="img" aria-label="Client B publishes a kind 7 reaction to relays; Client A subscribes with an #e filter and receives the tally">71 <rect class="d-actor" x="10" y="70" width="110" height="50" rx="8"/>72 <text class="d-actor-label" x="65" y="93" text-anchor="middle">Client B</text>73 <text class="d-note" x="65" y="110" text-anchor="middle">taps like</text>74 <rect class="d-actor" x="225" y="20" width="110" height="44" rx="8"/>75 <text class="d-actor-label" x="280" y="47" text-anchor="middle">Relay 1</text>76 <rect class="d-actor" x="225" y="126" width="110" height="44" rx="8"/>77 <text class="d-actor-label" x="280" y="153" text-anchor="middle">Relay 2</text>78 <rect class="d-actor" x="440" y="70" width="110" height="50" rx="8"/>79 <text class="d-actor-label" x="495" y="93" text-anchor="middle">Client A</text>80 <text class="d-note" x="495" y="110" text-anchor="middle">shows count</text>81 <path class="d-msg d-flow" d="M122,84 C170,70 180,52 223,44"/>82 <path class="d-msg d-flow" d="M122,106 C170,120 180,138 223,146"/>83 <path class="d-msg blue d-flow" d="M337,44 C380,52 390,70 438,84"/>84 <path class="d-msg blue d-flow" d="M337,146 C380,138 390,120 438,106"/>85 <text class="d-label" x="160" y="48" text-anchor="middle">EVENT kind 7</text>86 <text class="d-label" x="402" y="48" text-anchor="middle">REQ {"#e":[…]}</text>87 <circle r="4" fill="#f26a00">88 <animateMotion dur="2.6s" repeatCount="indefinite" path="M122,84 C170,70 180,52 223,44"/>89 </circle>90 <circle r="4" fill="#2e5f9e">91 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M337,44 C380,52 390,70 438,84"/>92 </circle>93 </svg>94 </div>95 <figcaption>Fig. 25-1 — <b>Reaction round trip.</b> Client B publishes one kind 7 event. Client A queries by <code>#e</code> and renders the count.</figcaption>96 </figure>9798 <h2 class="bar" id="spec"><span class="num">2</span> The Reaction Event (Kind 7)</h2>99 <p>A reaction is a <strong>kind 7</strong> event. The <code>content</code> field must hold user-generated content that states the value of the reaction. The convention is <code>+</code>, <code>-</code>, or an emoji.</p>100101 <div class="tscroll"><table class="ref">102 <caption>Content interpretation rules</caption>103 <tr><th>Content</th><th>Rule</th><th>Meaning</th></tr>104 <tr><td class="btn">+</td><td>MUST</td><td>Interpret as a "like" or "upvote".</td></tr>105 <tr><td class="btn">""</td><td>MUST</td><td>An empty string. Interpret it the same as <code>+</code>: a like or upvote.</td></tr>106 <tr><td class="btn">-</td><td>MUST</td><td>Interpret as a "dislike" or "downvote".</td></tr>107 <tr><td class="btn">emoji</td><td>SHOULD NOT</td><td>An emoji or a <a href="30.html">NIP-30</a> custom emoji. Do not interpret it as a like or a dislike. Clients may display the emoji on the post instead.</td></tr>108 </table></div>109110 <div class="box warn">111 <span class="tag">Warning!</span>112 <p>Do not fold emoji reactions into your like counter. The spec removed that mapping in 2025. A 🔥 is a 🔥, not an upvote.</p>113 </div>114115 <h2 class="bar" id="tags"><span class="num">3</span> Tags</h2>116 <p>A kind 7 reaction locates its target with tags. The tag names come from <a href="01.html">NIP-01</a>. Their rules here are:</p>117118 <div class="tscroll"><table class="ref">119 <caption>Kind 7 tag reference</caption>120 <tr><th>Tag</th><th>Rule</th><th>Value and notes</th></tr>121 <tr><td class="btn">e</td><td>MUST</td><td>The <code>id</code> of the event being reacted to. It should carry a relay hint that points to a relay where the target can be found. Extra <code>e</code> tags are not recommended; if present, the target <code>id</code> must be the last <code>e</code> tag.</td></tr>122 <tr><td class="btn">p</td><td>SHOULD</td><td>The <code>pubkey</code> of the author of the target event. Extra <code>p</code> tags are not recommended; if present, the target <code>pubkey</code> must be the last <code>p</code> tag.</td></tr>123 <tr><td class="btn">a</td><td>SHOULD</td><td>Only when the target is an addressable event. Set it to the coordinates <code>&lt;kind&gt;:&lt;pubkey&gt;:&lt;d-tag&gt;</code> of the target. Include it together with the <code>e</code> tag, not instead of it.</td></tr>124 <tr><td class="btn">k</td><td>MAY</td><td>The kind number of the target event as a string, for example <code>"1"</code>.</td></tr>125 </table></div>126127 <p>Hints help clients find the target. The <code>e</code> and <code>a</code> tags should include relay and pubkey hints. The <code>p</code> tags should include relay hints.</p>128129 <figure class="diagram">130 <div class="frame">131 <svg viewBox="0 0 560 200" role="img" aria-label="A reaction event points at three target types: a regular event via the e tag, an addressable event via the a tag, and external content via kind 17 i tags">132 <rect class="d-actor" x="10" y="75" width="130" height="50" rx="8"/>133 <text class="d-actor-label" x="75" y="98" text-anchor="middle">Reaction</text>134 <text class="d-note" x="75" y="115" text-anchor="middle">kind 7 / 17</text>135 <rect class="d-actor" x="330" y="14" width="220" height="44" rx="8"/>136 <text class="d-actor-label" x="440" y="34" text-anchor="middle">Regular event</text>137 <text class="d-note" x="440" y="50" text-anchor="middle">e = id (+ p, k)</text>138 <rect class="d-actor" x="330" y="78" width="220" height="44" rx="8"/>139 <text class="d-actor-label" x="440" y="98" text-anchor="middle">Addressable event</text>140 <text class="d-note" x="440" y="114" text-anchor="middle">a = kind:pubkey:d-tag, plus e</text>141 <rect class="d-actor" x="330" y="142" width="220" height="44" rx="8"/>142 <text class="d-actor-label" x="440" y="162" text-anchor="middle">External content</text>143 <text class="d-note" x="440" y="178" text-anchor="middle">kind 17 - NIP-73 k + i tags</text>144 <path class="d-msg" d="M142,88 C230,72 250,48 327,38"/>145 <path class="d-msg" d="M142,100 L327,100"/>146 <path class="d-msg blue" d="M142,112 C230,128 250,152 327,162"/>147 <text class="d-label" x="230" y="60" text-anchor="middle">e tag</text>148 <text class="d-label" x="230" y="93" text-anchor="middle">a + e tags</text>149 <text class="d-label" x="230" y="148" text-anchor="middle">i tag</text>150 </svg>151 </div>152 <figcaption>Fig. 25-2 — <b>Target map.</b> Kind 7 points at Nostr events. Kind 17 points at external content by identifier.</figcaption>153 </figure>154155 <p>The source NIP ships this reference routine. It builds a like for one target event with the recommended tags and hints:</p>156 <div class="crt">157 <div class="titlebar">Example code - build a like event (Swift)</div>158<pre><span class="k">func</span> make_like_event(pubkey: String, privkey: String, liked: NostrEvent, hint: String) -&gt; NostrEvent {159 <span class="k">var</span> tags: [[String]] = []160 tags.append([<span class="s">"e"</span>, liked.id, hint, liked.pubkey])161 tags.append([<span class="s">"p"</span>, liked.pubkey, hint])162 tags.append([<span class="s">"k"</span>, String(liked.kind)])163 <span class="k">let</span> ev = NostrEvent(content: <span class="s">"+"</span>, pubkey: pubkey, kind: <span class="s">7</span>, tags: tags)164 ev.calculate_id()165 ev.sign(privkey: privkey)166 <span class="k">return</span> ev167}</pre>168 </div>169170 <div class="box tech">171 <span class="tag">Tech Note</span>172 <p>The source has two rough edges. First, the Tags section says an <code>e</code> tag "MUST always" be present, but kind 17 external reactions have no Nostr event to point at; the rule can only apply to kind 7. Second, the <code>k</code> tag changes meaning between kinds: on kind 7 it holds a stringified kind number, on kind 17 it holds a <a href="73.html">NIP-73</a> external content type such as <code>web</code>.</p>173 </div>174175 <h2 class="bar" id="external"><span class="num">4</span> External Content Reactions (Kind 17)</h2>176 <p>Sometimes the target is not a Nostr event. It is a web page, a podcast, or another external resource. In that case the reaction must be a <strong>kind 17</strong> event. It must include <a href="73.html">NIP-73</a> external content <code>k</code> and <code>i</code> tags that reference the content. The <code>k</code> tag names the content type. The <code>i</code> tag holds the identifier, with an optional URL hint.</p>177178 <div class="crt">179 <div class="titlebar">Kind 17 - reacting to a website</div>180<pre>{181 <span class="k">"kind"</span>: 17,182 <span class="k">"content"</span>: <span class="s">"⭐"</span>,183 <span class="k">"tags"</span>: [184 [<span class="s">"k"</span>, <span class="s">"web"</span>],185 [<span class="s">"i"</span>, <span class="s">"https://example.com"</span>]186 ]187}</pre>188 </div>189190 <p>A target can carry more than one identifier pair. This reaction likes one episode of one podcast. Each <code>i</code> tag adds a URL hint as its third element:</p>191 <div class="crt">192 <div class="titlebar">Kind 17 - reacting to a podcast episode</div>193<pre>{194 <span class="k">"kind"</span>: 17,195 <span class="k">"content"</span>: <span class="s">"+"</span>,196 <span class="k">"tags"</span>: [197 [<span class="s">"k"</span>, <span class="s">"podcast:guid"</span>],198 [<span class="s">"i"</span>, <span class="s">"podcast:guid:917393e3-1b1e-5cef-ace4-edaa54e1f810"</span>,199 <span class="s">"https://fountain.fm/show/QRT0l2EfrKXNGDlRrmjL"</span>],200 [<span class="s">"k"</span>, <span class="s">"podcast:item:guid"</span>],201 [<span class="s">"i"</span>, <span class="s">"podcast:item:guid:PC20-229"</span>,202 <span class="s">"https://fountain.fm/episode/DQqBg5sD3qFGMCZoSuLF"</span>]203 ]204}</pre>205 </div>206207 <h2 class="bar" id="emoji"><span class="num">5</span> Custom Emoji Reactions</h2>208 <p>A client may put a <a href="30.html">NIP-30</a> custom emoji <code>:shortcode:</code> in the reaction <code>content</code>. A matching <code>emoji</code> tag maps the shortcode to an image URL. When a shortcode is present, the client should read the <code>emoji</code> tag and render the content as that emoji.</p>209 <div class="crt">210 <div class="titlebar">Kind 7 - custom emoji reaction</div>211<pre>{212 <span class="k">"kind"</span>: 7,213 <span class="k">"content"</span>: <span class="s">":soapbox:"</span>,214 <span class="k">"tags"</span>: [215 [<span class="s">"emoji"</span>, <span class="s">"soapbox"</span>, <span class="s">"https://gleasonator.com/emoji/Gleasonator/soapbox.png"</span>]216 ]217 <span class="c">// other fields...</span>218}</pre>219 </div>220 <p>Two limits apply. The <code>content</code> can hold only one <code>:shortcode:</code>. The event should carry only one <code>emoji</code> tag.</p>221222 <h2 class="bar" id="examples"><span class="num">6</span> Worked Example</h2>223 <p>Alice publishes a kind 1 note. Bob likes it. The full reaction event and the relay session look like this:</p>224 <div class="crt">225 <div class="titlebar">Complete kind 7 event - Bob likes Alice's note</div>226<pre>{227 <span class="k">"id"</span>: <span class="s">"9d3b1c44f0…"</span>,228 <span class="k">"pubkey"</span>: <span class="s">"8c4f2ab9d1…"</span>, <span class="c">// Bob</span>229 <span class="k">"created_at"</span>: 1756713600,230 <span class="k">"kind"</span>: 7,231 <span class="k">"tags"</span>: [232 [<span class="s">"e"</span>, <span class="s">"b1a649ebe8…"</span>, <span class="s">"wss://relay.example.com"</span>, <span class="s">"22a12a12…"</span>],233 [<span class="s">"p"</span>, <span class="s">"22a12a12…"</span>, <span class="s">"wss://relay.example.com"</span>],234 [<span class="s">"k"</span>, <span class="s">"1"</span>]235 ],236 <span class="k">"content"</span>: <span class="s">"+"</span>,237 <span class="k">"sig"</span>: <span class="s">"7f0ac2e91b…"</span>238}</pre>239 </div>240 <div class="crt">241 <div class="titlebar">Session transcript - wss://relay.example.com</div>242<pre><span class="c">-- Bob publishes the reaction --</span>243<span class="send">→ ["EVENT", {"id":"9d3b1c44f0…","kind":7,"content":"+",244 "tags":[["e","b1a649ebe8…","wss://relay.example.com","22a12a12…"],245 ["p","22a12a12…","wss://relay.example.com"],["k","1"]], …}]</span>246<span class="recv">← ["OK", "9d3b1c44f0…", true, ""]</span>247248<span class="c">-- Alice's client counts reactions to her note --</span>249<span class="send">→ ["REQ", "reacts", {"kinds":[7], "#e":["b1a649ebe8…"]}]</span>250<span class="recv">← ["EVENT", "reacts", {"id":"9d3b1c44f0…", "kind":7, "content":"+", …}]</span>251<span class="recv">← ["EOSE", "reacts"]</span>252<span class="c">-- content "+" or "" → like. "-" → dislike. emoji → shown as-is --</span>253<span class="send">→ ["CLOSE", "reacts"]</span></pre>254 </div>255256 <h2 class="bar" id="history"><span class="num">7</span> Version History</h2>257 <p>NIP-25 started as a likes-and-dislikes spec for text notes. It grew to cover any event, custom emoji, and external content. Notable changes, newest first:</p>258 <ul class="patchnotes">259 <li><span class="ver">2025</span><span class="when">2025-08</span>Added external content reactions: kind 17 with <a href="73.html">NIP-73</a> <code>k</code> and <code>i</code> tags.</li>260 <li><span class="ver">2025</span><span class="when">2025-05</span>Removed the rule that mapped emoji reactions to like or dislike. Added relay and pubkey hint recommendations.</li>261 <li><span class="ver">2025</span><span class="when">2025-02</span>Discouraged extra <code>e</code> and <code>p</code> tags; the target must be the last tag of its letter.</li>262 <li><span class="ver">2024</span><span class="when">2024-05</span>Added the <code>a</code> tag for addressable targets and the <code>k</code> kind tag. A broader "Simplifying reactions" rewrite was reverted in June.</li>263 <li><span class="ver">2023</span><span class="when">2023-08</span>An empty <code>content</code> string now counts as <code>+</code>.</li>264 <li><span class="ver">2023</span><span class="when">2023-07</span>Added custom emoji reactions with <a href="30.html">NIP-30</a> shortcodes.</li>265 <li class="minor"><span class="ver">2023</span><span class="when">2023-01</span>Allowed emojis to count as likes or dislikes (removed again in 2025).</li>266 <li><span class="ver">2022</span><span class="when">2022-08</span>Reactions can target any event, not only text notes. The generic like became an explicit <code>+</code>.</li>267 <li><span class="ver">Origin</span><span class="when">2022-07</span>William Casarin created NIP-25 with likes, dislikes, and downvotes (2022-07-30).</li>268 </ul>269270 <div class="credits">271 <h3 class="cardhead">Original Proposal - Credits</h3>272 <table>273 <tr><td>Author</td><td>William Casarin</td></tr>274 <tr><td>First commit</td><td>2022-07-30 — “NIP-25: Reactions”</td></tr>275 <tr><td>Status</td><td>draft - optional — clients choose whether to show or send reactions</td></tr>276 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/25.md">25.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/25.md">full change history</a></td></tr>277 </table>278 </div>279280 <h2 class="bar"><span class="num">8</span> See Also</h2>281 <ul>282 <li><a href="01.html">NIP-01</a> — the base event format and the <code>e</code>, <code>p</code>, <code>a</code> tags reused here.</li>283 <li><a href="30.html">NIP-30</a> — custom emoji: the <code>:shortcode:</code> and <code>emoji</code> tag format.</li>284 <li><a href="73.html">NIP-73</a> — external content IDs: the <code>k</code> and <code>i</code> tags required by kind 17.</li>285 <li><a href="10.html">NIP-10</a> — text notes and threads, the most common reaction targets.</li>286 <li><a href="18.html">NIP-18</a> — reposts, the other lightweight response to a note.</li>287 </ul>288 </div>289290 <aside>291 <div class="cartridge">292 <div class="inner">293 <h3 class="cardhead">Spec Card</h3>294 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>295 <dl>296 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>297 <dt>Defines</dt><dd>reaction events - content verdicts - <code>k</code> kind tag - custom emoji use</dd>298 <dt>Kinds</dt><dd>7 (reaction) - 17 (external content reaction)</dd>299 <dt>Requires</dt><dd><a href="01.html">NIP-01</a>; uses <a href="30.html">NIP-30</a>, <a href="73.html">NIP-73</a></dd>300 <dt>Transport</dt><dd>plain events - no new messages</dd>301 <dt>First commit</dt><dd>2022-07-30</dd>302 <dt>Last updated</dt><dd>2026-06-13</dd>303 </dl>304 </div>305 </div>306 </aside>307 </main>308309 <nav class="navbtns">310 <a href="24.html">◀ NIP-24: Extra metadata fields and tags</a>311 <a href="26.html">NIP-26: Delegated Event Signing ▶</a>312 </nav>313314 <footer class="pagefoot">315 <a href="../index.html">Nostr Protocol Manual</a>316 <span class="pageno">NIP 25 / F4</span>317 </footer>318</div>319320<script src="../assets/webmcp.js" defer></script>321</body>322</html>