Chapter 25 - Social
25
Nostr Implementation Possibility - Hex № 25

Reactions

One-tap responses to any event: likes, dislikes, and emoji. The smallest social signal in the protocol.

1 Overview

A reaction is a small event that points at another event and carries a short verdict. The verdict is a +, a -, or an emoji. Clients use reactions to build like buttons, upvote counters, and emoji rows.

This NIP defines two event kinds. Kind 7 reacts to a native Nostr event. Kind 17 reacts to external content, such as a website or a podcast episode. Both kinds are ordinary signed events from NIP-01. Any relay can store them. Any client can count them.

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 e tag matches the note's id.

Hint!

Reactions are cheap to publish and cheap to query. One filter — {"kinds":[7],"#e":[<id>]} — returns every reaction to a note that the relay knows about.

Client B taps like Relay 1 Relay 2 Client A shows count EVENT kind 7 REQ {"#e":[…]}
Fig. 25-1 — Reaction round trip. Client B publishes one kind 7 event. Client A queries by #e and renders the count.

2 The Reaction Event (Kind 7)

A reaction is a kind 7 event. The content field must hold user-generated content that states the value of the reaction. The convention is +, -, or an emoji.

Content interpretation rules
ContentRuleMeaning
+MUSTInterpret as a "like" or "upvote".
""MUSTAn empty string. Interpret it the same as +: a like or upvote.
-MUSTInterpret as a "dislike" or "downvote".
emojiSHOULD NOTAn emoji or a NIP-30 custom emoji. Do not interpret it as a like or a dislike. Clients may display the emoji on the post instead.
Warning!

Do not fold emoji reactions into your like counter. The spec removed that mapping in 2025. A 🔥 is a 🔥, not an upvote.

3 Tags

A kind 7 reaction locates its target with tags. The tag names come from NIP-01. Their rules here are:

Kind 7 tag reference
TagRuleValue and notes
eMUSTThe id of the event being reacted to. It should carry a relay hint that points to a relay where the target can be found. Extra e tags are not recommended; if present, the target id must be the last e tag.
pSHOULDThe pubkey of the author of the target event. Extra p tags are not recommended; if present, the target pubkey must be the last p tag.
aSHOULDOnly when the target is an addressable event. Set it to the coordinates <kind>:<pubkey>:<d-tag> of the target. Include it together with the e tag, not instead of it.
kMAYThe kind number of the target event as a string, for example "1".

Hints help clients find the target. The e and a tags should include relay and pubkey hints. The p tags should include relay hints.

Reaction kind 7 / 17 Regular event e = id (+ p, k) Addressable event a = kind:pubkey:d-tag, plus e External content kind 17 - NIP-73 k + i tags e tag a + e tags i tag
Fig. 25-2 — Target map. Kind 7 points at Nostr events. Kind 17 points at external content by identifier.

The source NIP ships this reference routine. It builds a like for one target event with the recommended tags and hints:

Example code - build a like event (Swift)
func make_like_event(pubkey: String, privkey: String, liked: NostrEvent, hint: String) -> NostrEvent {
    var tags: [[String]] = []
    tags.append(["e", liked.id, hint, liked.pubkey])
    tags.append(["p", liked.pubkey, hint])
    tags.append(["k", String(liked.kind)])
    let ev = NostrEvent(content: "+", pubkey: pubkey, kind: 7, tags: tags)
    ev.calculate_id()
    ev.sign(privkey: privkey)
    return ev
}
Tech Note

The source has two rough edges. First, the Tags section says an e 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 k tag changes meaning between kinds: on kind 7 it holds a stringified kind number, on kind 17 it holds a NIP-73 external content type such as web.

4 External Content Reactions (Kind 17)

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 kind 17 event. It must include NIP-73 external content k and i tags that reference the content. The k tag names the content type. The i tag holds the identifier, with an optional URL hint.

Kind 17 - reacting to a website
{
  "kind": 17,
  "content": "⭐",
  "tags": [
    ["k", "web"],
    ["i", "https://example.com"]
  ]
}

A target can carry more than one identifier pair. This reaction likes one episode of one podcast. Each i tag adds a URL hint as its third element:

Kind 17 - reacting to a podcast episode
{
  "kind": 17,
  "content": "+",
  "tags": [
    ["k", "podcast:guid"],
    ["i", "podcast:guid:917393e3-1b1e-5cef-ace4-edaa54e1f810",
         "https://fountain.fm/show/QRT0l2EfrKXNGDlRrmjL"],
    ["k", "podcast:item:guid"],
    ["i", "podcast:item:guid:PC20-229",
         "https://fountain.fm/episode/DQqBg5sD3qFGMCZoSuLF"]
  ]
}

5 Custom Emoji Reactions

A client may put a NIP-30 custom emoji :shortcode: in the reaction content. A matching emoji tag maps the shortcode to an image URL. When a shortcode is present, the client should read the emoji tag and render the content as that emoji.

Kind 7 - custom emoji reaction
{
  "kind": 7,
  "content": ":soapbox:",
  "tags": [
    ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"]
  ]
  // other fields...
}

Two limits apply. The content can hold only one :shortcode:. The event should carry only one emoji tag.

6 Worked Example

Alice publishes a kind 1 note. Bob likes it. The full reaction event and the relay session look like this:

Complete kind 7 event - Bob likes Alice's note
{
  "id":         "9d3b1c44f0…",
  "pubkey":     "8c4f2ab9d1…",  // Bob
  "created_at": 1756713600,
  "kind":       7,
  "tags": [
    ["e", "b1a649ebe8…", "wss://relay.example.com", "22a12a12…"],
    ["p", "22a12a12…", "wss://relay.example.com"],
    ["k", "1"]
  ],
  "content":    "+",
  "sig":        "7f0ac2e91b…"
}
Session transcript - wss://relay.example.com
-- Bob publishes the reaction --
→ ["EVENT", {"id":"9d3b1c44f0…","kind":7,"content":"+",
     "tags":[["e","b1a649ebe8…","wss://relay.example.com","22a12a12…"],
             ["p","22a12a12…","wss://relay.example.com"],["k","1"]], …}]
← ["OK", "9d3b1c44f0…", true, ""]

-- Alice's client counts reactions to her note --
→ ["REQ", "reacts", {"kinds":[7], "#e":["b1a649ebe8…"]}]
← ["EVENT", "reacts", {"id":"9d3b1c44f0…", "kind":7, "content":"+", …}]
← ["EOSE", "reacts"]
-- content "+" or "" → like. "-" → dislike. emoji → shown as-is --
→ ["CLOSE", "reacts"]

7 Version History

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:

  • 20252025-08Added external content reactions: kind 17 with NIP-73 k and i tags.
  • 20252025-05Removed the rule that mapped emoji reactions to like or dislike. Added relay and pubkey hint recommendations.
  • 20252025-02Discouraged extra e and p tags; the target must be the last tag of its letter.
  • 20242024-05Added the a tag for addressable targets and the k kind tag. A broader "Simplifying reactions" rewrite was reverted in June.
  • 20232023-08An empty content string now counts as +.
  • 20232023-07Added custom emoji reactions with NIP-30 shortcodes.
  • 20232023-01Allowed emojis to count as likes or dislikes (removed again in 2025).
  • 20222022-08Reactions can target any event, not only text notes. The generic like became an explicit +.
  • Origin2022-07William Casarin created NIP-25 with likes, dislikes, and downvotes (2022-07-30).

Original Proposal - Credits

AuthorWilliam Casarin
First commit2022-07-30 — “NIP-25: Reactions”
Statusdraft - optional — clients choose whether to show or send reactions
Source25.md on GitHub - full change history

8 See Also

  • NIP-01 — the base event format and the e, p, a tags reused here.
  • NIP-30 — custom emoji: the :shortcode: and emoji tag format.
  • NIP-73 — external content IDs: the k and i tags required by kind 17.
  • NIP-10 — text notes and threads, the most common reaction targets.
  • NIP-18 — reposts, the other lightweight response to a note.