npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nostr-comments

v0.7.0

Published

A Nostr-based web comment component using NIP-22 protocol

Readme

Nostr Comments

A web comment component based on the Nostr network, implementing the NIP-22 protocol (Web part), using the I tag to identify URLs.

Features

  • NIP-22 Protocol: Uses kind:1111 events, supports comments and replies
  • Multiple Login Methods:
    • NIP-07 browser extensions (Alby, nos2x, etc.)
    • NIP-46 Bunker remote signing
    • Temporary account (locally generated key pair)
  • Multilingual: 20 languages built-in (en, zh-CN, zh-TW, ja, ko, es, fr, de, pt, ru, ar, it, nl, pl, tr, vi, th, id, hi, uk), supports custom translations
  • Themes: Light, dark, auto (follows system), easy customization with a few CSS variables
  • Headless Mode: Disable default styles for fully custom UI

Installation

npm install nostr-comments

Quick Start

React/Preact Usage

import { NostrComments } from "nostr-comments";

function App() {
  return <NostrComments url="https://example.com/blog/my-post" locale="en" />;
}

API

NostrComments Component Props

| Prop | Type | Default | Description | | -------------------- | ----------------------------------- | ------------------ | ---------------------------------------------------------- | | url | string | Required | Web page URL, used to identify comment scope | | mention | string | - | Public key to mention in comments (receives notifications) | | relays | string[] | Default relay list | List of Nostr relay addresses | | pageSize | number | 50 | Number of comments per page | | locale | string | Auto-detect | Interface language (20 languages supported) | | translations | Partial<Translations> | - | Custom translation text | | theme | 'light' \| 'dark' \| 'auto' | 'auto' | Theme mode | | headless | boolean | false | Enable Headless mode | | classNames | object | - | Custom CSS class names | | signer | Signer | - | External signer instance (skips login modal when provided) | | enabledSigners | ('nip07' \| 'bunker' \| 'temp')[] | All enabled | Enabled login methods | | pow | number | 18 | POW difficulty (leading zero bits) to prevent spam | | onCommentPublished | (event: NostrEvent) => void | - | Callback when comment is published | | onError | (error: Error) => void | - | Error callback |

classNames Object

{
  root?: string       // Root container
  list?: string       // Comment list
  item?: string       // Comment item
  editor?: string     // Editor
  loginModal?: string // Login modal
}

Examples

Custom Relays

<NostrComments
  url="https://example.com/post/123"
  relays={["wss://relay.damus.io", "wss://relay.nostr.band", "wss://nos.lol"]}
/>

Dark Theme

<NostrComments url="https://example.com/post/123" theme="dark" />

Custom Translations

<NostrComments
  url="https://example.com/post/123"
  locale="en"
  translations={{
    comments: "Discussion",
    writeComment: "Share your thoughts...",
    publish: "Post",
    noComments: "Start the conversation!",
    keyCompatibility: "This key works with any Nostr app!",
  }}
/>

Anti-Spam with POW

<NostrComments url="https://example.com/post/123" pow={18} />

Requires commenters to compute a proof-of-work (NIP-13) before publishing. Higher values = more computation time = stronger spam prevention. Recommended: 10-20 bits.

Extension Login Only

<NostrComments url="https://example.com/post/123" enabledSigners={["nip07"]} />

External Signer

Pass your own signer instance to skip the built-in login modal:

import { NostrComments, Nip07Signer, TempSigner } from "nostr-comments";

// Use NIP-07 extension
const signer = new Nip07Signer();

// Or use a temporary signer
const signer = new TempSigner();

// Or any object that implements the Signer interface
const customSigner = {
  getPublicKey: async () => "your-pubkey",
  signEvent: async (event) => signedEvent,
};

<NostrComments url="https://example.com/post/123" signer={signer} />;

Headless Mode

<NostrComments
  url="https://example.com/post/123"
  headless={true}
  classNames={{
    root: "my-comments",
    list: "my-comments-list",
    editor: "my-editor",
  }}
/>

CSS Variables

The component uses CSS variables for theme customization. Only a few base colors need to be set — all secondary, hover, border, and muted colors are automatically derived via color-mix().

Base Colors (override these to customize)

.nostr-comments {
  --nc-bg: #ffffff; /* Background for inputs, modals */
  --nc-text: #1f2937; /* Primary text color */
  --nc-primary: #2563eb; /* Accent/brand color */
  --nc-error: #dc2626; /* Error states */
  --nc-success: #16a34a; /* Success states */
}

All other color variables (--nc-bg-secondary, --nc-bg-hover, --nc-border, --nc-text-secondary, --nc-text-muted, --nc-primary-hover, --nc-primary-light, --nc-error-light, --nc-success-light) are derived automatically from the base colors.

Custom Theme Example

/* A custom purple theme — works for both light and dark */
.nostr-comments {
  --nc-bg: #1a1a2e;
  --nc-text: #eaeaea;
  --nc-primary: #e94560;
}

For dark mode, override the base colors under [data-theme="dark"] or [data-theme="auto"] with prefers-color-scheme: dark. The derived colors will adjust automatically.

Other Variables

.nostr-comments {
  /* Spacing */
  --nc-space-xs: 4px;
  --nc-space-sm: 8px;
  --nc-space-md: 12px;
  --nc-space-lg: 16px;
  --nc-space-xl: 24px;

  /* Border Radius */
  --nc-radius-sm: 8px;
  /* button, textarea radius */
  --nc-radius-md: 12px;
  --nc-radius-lg: 16px;
  /* modal radius */
  --nc-radius-xl: 24px;

  /* Avatar Border Radius */
  --nc-avatar-radius: 50%;
}

Advanced Usage

Using Standalone Hooks

import { useComments, useSigner } from "nostr-comments";

function CustomComments({ url }) {
  const { comments, loading, addEvent } = useComments({ url });
  const { signer, loginWithNip07 } = useSigner();

  // Custom UI...
}

Using Service Functions

import { publishComment, subscribeComments } from "nostr-comments";

// Publish a comment
const event = await publishComment(signer, relays, {
  url: "https://example.com/post",
  content: "Great post!",
});

// Subscribe to comments
const unsubscribe = subscribeComments({
  url: "https://example.com/post",
  relays,
  onEvent: (event) => console.log("New comment:", event),
});

Custom Signers

import { Nip07Signer, TempSigner, BunkerSigner } from "nostr-comments";

// NIP-07 extension
const nip07 = new Nip07Signer();
const pubkey = await nip07.getPublicKey();

// Temporary account
const temp = new TempSigner();
const nsec = temp.getNsec(); // Export private key
temp.downloadKeyFile(); // Download key file

// Bunker
const bunker = BunkerSigner.create("bunker://pubkey?relay=wss://...");
await bunker.connect();

NIP-22 Event Format

Top-level Comment

{
  "kind": 1111,
  "content": "Nice article!",
  "tags": [
    ["I", "https://example.com/post/123"],
    ["K", "web"],
    ["i", "https://example.com/post/123"],
    ["k", "web"]
  ]
}

Reply Comment

{
  "kind": 1111,
  "content": "I agree!",
  "tags": [
    ["I", "https://example.com/post/123"],
    ["K", "web"],
    ["e", "<parent_event_id>", "<relay_hint>", "<parent_pubkey>"],
    ["k", "1111"],
    ["p", "<parent_pubkey>"]
  ]
}

Development

# Install dependencies
npm install

# Start development server
npm run dev

# Build
npm run build

License

MIT