Nostr Web Components

Simple, reusable web components for Nostr integration

Overview

Nostr Web Components provides a collection of simple, reusable web components for integrating Nostr functionality into static or server-rendered HTML websites. This documentation covers installation guides and all available components.

Guides

Components

Guides

Introduction

Nostr is very simple, but not simple enough that you would write a full-blown client from scratch in pure JavaScript every time what you just need is to dynamically embed a Nostr note in your otherwise static HTML site; or display a button asking people to RSVP in the static site you made for a local meetup; or even for when you just have a list of npubs and you want to display their names.

Nostr Web Components is not trying to be a framework for writing Nostr clients. For that you probably still want something like Svelte, or React, or Solid, or whatever component together with applesauce or ndk or nostr-gadgets or some other library.

For all other situations when you just want little bits of Nostr you can use these web components. Although they're isolated in a shadow DOM and completely unstyled, they come with part attributes that you can use to style them from the outside. That also works with TailwindCSS.

Installation

There are four ways to use Nostr Web Components in your project:

1. Standalone Script Import

The simplest way is to import individual components directly from jsdelivr:

<script src="https://cdn.jsdelivr.net/npm/nostr-web-components/dist/nostr-name.js"></script>
<nostr-name pubkey="npub..."></nostr-name>

2. Complete Bundle Import

If you want to use multiple components, you can import the complete bundle:

<script src="https://cdn.jsdelivr.net/npm/nostr-web-components/dist/index.js"></script>
<nostr-name pubkey="npub..."></nostr-name>
<nostr-picture pubkey="npub..."></nostr-picture>

3. Using native ES module imports and importmap

If you are using more than one component and not making your own bundle, then this approach is what you're looking for as it has many advantages:

  • Efficient Loading: Only load the components you need
  • Shared Dependencies: Dependencies like @nostr/tools are loaded once and shared between components
  • Better Caching: Browser can cache individual components and dependencies separately
  • Modern Standards: Uses native ES modules with proper dependency resolution
<script type="importmap">
  {
    "imports": {
      "@nostr/tools/": "https://esm.sh/*jsr/@nostr/[email protected]/",
      "@nostr/gadgets/": "https://esm.sh/*jsr/@nostr/[email protected]/",
      "@noble/curves/": "https://esm.sh/*@noble/[email protected]/",
      "@noble/hashes/": "https://esm.sh/*@noble/[email protected]/",
      "@scure/base/": "https://esm.sh/*@scure/[email protected]/",
      "@scure/base": "https://esm.sh/*@scure/[email protected]",
      "idb-keyval": "https://esm.sh/[email protected]"
    },
    "scopes": {
      "https://esm.sh/": {
        "@jsr/nostr__tools/": "https://esm.sh/*@jsr/[email protected]/",
        "@jsr/nostr__gadgets/": "https://esm.sh/*@jsr/[email protected]/",
        "@jsr/fiatjaf__lru-cache/": "https://esm.sh/jsr/@fiatjaf/[email protected]/"
      }
    }
  }
</script>

<script type="module">
  import 'https://esm.sh/nostr-web-components/lib/nostr-name.js'
  import 'https://esm.sh/nostr-web-components/lib/nostr-picture.js'
</script>

4. NPM Installation

For projects using npm:

npm install nostr-web-components

Then import components in your JavaScript:

import 'nostr-web-components/nostr-name'
import 'nostr-web-components/nostr-picture'

Custom Templates

Some components that render complex data (like <nostr-note>, for example) provide a default template that can be styled using part selectors, but also allow you to pass your own HTML template if you need a different hierarchy.

The template must be defined outside the component and then referenced by an id:

<template id="my-note">
  <div>
    <a part="author-link">
      <span part="author-name"></span>
    </a>
  </div>
  <div part="content"></div>
  <div>
    <a part="link">
      <time part="date"></time>
    </a>
  </div>
</template>

<nostr-note template="my-note" ref="nevent..."></nostr-note>

Usage with @nostr/gadgets

These components are all using the @nostr/gadgets library's global pool and shared metadata loaders and relay list loaders, which means that if you're using those same bits anywhere else in your code those resources will be shared efficiently and relay connections won't be duplicated nor resources have to be wasted on duplicated downloads, everything will be reused, including the local IndexedDB cache of profile metadata and relay lists.

That means you can reuse components like <nostr-text> or <nostr-name> inside heavy JavaScript Nostr clients with no fear of bloat or efficiency loss (other components too, but I imagine these simpler ones are the most useful in such contexts).

Even if you are not using @nostr/gadgets but you are using a global @nostr/tools Pool you could still import gadgets' global Pool or call global.setPool() so the same connections are reused by these components.

Styling with Tailwind

Nostr Web Components ship with a bunch of part attributes that can be used by CSS to break the shadow DOM barrier and style the component from the outside.

To do that with Tailwind we just need a simple plugin that tells Tailwind to transform part-[<part-attribute>]:actual-tw-class into the proper CSS declaration. In short, add this to your Tailwind v4 CSS file:

@import "tailwindcss";
@plugin "tailwind-part";

And install it with npm: npm install --save-dev tailwind-part.

After that you can style components by placing classes on the component tag like this:

<nostr-picture pubkey="npub1..." class="part-[img]:rounded"></nostr-picture>

Check each component's documentation to see what part selectors are available for each.

Do it manually

Or you can declare the plugin manually in your Tailwind config or on a separate file that can be imported by the CSS:

import plugin from 'tailwindcss/plugin'

export default plugin(({ matchVariant }) => {
  matchVariant('part', value => {
    return `&::part(${value})`
  })
})

Yes, this is the full source code for the plugin.

Components

<nostr-name>

Displays a Nostr user's name from their pubkey.

Attributes

  • pubkey: The Nostr public key (npub or hex format) of the user

Styling Parts

This component exposes no styling parts.

Template Support

This component does not support external templates.

Hello,

<nostr-picture>

Displays a Nostr user's profile picture.

Attributes

  • pubkey: The Nostr public key (npub or hex format) of the user
  • width: (optional) Width of the image
  • height: (optional) Height of the image

Styling Parts

  • img: The image element showing the profile picture

Template Support

This component does not support external templates.

Profile picture with rounded border

<nostr-follow>

A button that allows website visitors to follow a Nostr profile with one click. If NIP-07 window.nostr is not present, it will be automatically included when the button is clicked.

Attributes

  • pubkey: The Nostr public key (npub or hex format) of the user to follow

Styling Parts

  • button: The follow button container
  • error-message: Error message text when follow fails

Slots

  • Default: Main button text
  • loading: Shown while follow request is processing
  • success: Shown after successful follow
  • failure: Shown if follow request fails

Events

  • success: Fired when follow is successful. Event detail includes already: boolean
  • failure: Fired when follow fails. Event detail includes error message

Template Support

This component does not support external templates.

Follow me on Nostr Following... ✓ Followed! Error:

<nostr-text>

Renders "plaintext" (NUML - Nostr Unassuming Markup Language, NIP-27) content with special rendering for NIP-21 nostr: references, relay URLs, and media.

Attributes

  • content: The text content to render with Nostr-specific formatting

Styling Parts

  • video: Video elements
  • audio: Audio elements
  • image: Image elements
  • reference: References to other notes/users
  • url: URL links
  • relay: Relay URLs

Template Support

This component does not support external templates.

<nostr-rsvp>

Displays information about a NIP-52 calendar event and allows users to RSVP. If window.nostr isn't present, it will be included automatically after user clicks.

Attributes

  • ref: Event reference (naddr format)

Styling Parts

  • container: Main container
  • title: Event title
  • time: Event time
  • description: Event description
  • buttons: RSVP buttons container
  • button: Individual button
  • accepted: Accepted button state
  • tentative: Tentative button state
  • declined: Declined button state

Slots

  • declined: Custom text for decline button
  • rsvp-sent: Custom message shown after RSVP is sent

Template Support

This component does not support external templates.

Decline RSVP Sent!

<nostr-livestream>

Displays metadata and renders the actual video of a NIP-53 livestream.

Attributes

  • ref: Livestream reference (naddr format)
  • template: ID of template element for custom rendering

Styling Parts

  • error: Error message container
  • header: Stream header section
  • image: Stream thumbnail
  • title: Stream title
  • status: Stream status
  • time: Stream time information
  • summary: Stream description
  • stats: Statistics section
  • current: Current viewers section
  • current-value: Current viewer count
  • video: Video player
  • participants: Participants section

Slots

  • error-pointer: Custom message when event pointer is invalid
  • error-fetch: Custom message when event cannot be fetched
  • error-invalid: Custom message when event is not a livestream
  • stats: Custom statistics display

Template Support

Supports external templates through the template attribute.

invalid code! couldn't find the event! not a livestream!

<nostr-note>

Displays a Nostr note (kind:1 or kind:1111 or similar) with NIP-27 text formatting, metadata about the author, parent/root, formatted time and permalink.

Attributes

  • ref: Note reference (nevent format)
  • event: Raw event JSON
  • relays: Comma-separated list of relay URLs
  • template: ID of template element for custom rendering

Styling Parts

  • container: The main container
  • header: Note header section
  • author-link: Author name/profile link
  • author-name: Author's display name
  • author-npub-short: Shortened npub
  • parent-link: Link to parent note
  • link: Note permalink
  • content: Note content
  • reference: Referenced content
  • url: URL links
  • image: Images
  • video: Video content
  • audio: Audio content
  • relay: Relay references

Template Support

Supports external templates through the template attribute.

<nostr-event-json>

Displays a Nostr event in JSON format.

Attributes

  • ref: Event reference (nevent/naddr/note format)

Styling Parts

  • pre: Container element
  • key: JSON key styling
  • kind: Event kind value
  • content: Event content
  • id: Event ID
  • pubkey: Public key
  • sig: Signature

Template Support

This component does not support external templates.