Simple, reusable web components for Nostr integration
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.
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.
There are four ways to use Nostr Web Components in your project:
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>
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>
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:
@nostr/tools are loaded once and
shared between components
<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>
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'
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>
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.
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.
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.
Displays a Nostr user's name from their pubkey.
pubkey: The Nostr public key (npub or hex format)
of the user
This component exposes no styling parts.
This component does not support external templates.
Displays a Nostr user's profile picture.
pubkey: The Nostr public key (npub or hex format)
of the user
width: (optional) Width of the imageheight: (optional) Height of the imageimg: The image element showing the profile
picture
This component does not support external templates.
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.
pubkey: The Nostr public key (npub or hex format)
of the user to follow
button: The follow button containererror-message: Error message text when follow
fails
loading: Shown while follow request is processing
success: Shown after successful followfailure: Shown if follow request failssuccess: Fired when follow is successful. Event
detail includes already: boolean
failure: Fired when follow fails. Event detail
includes error message
This component does not support external templates.
Renders "plaintext" (NUML - Nostr Unassuming Markup Language, NIP-27) content with special rendering for NIP-21 nostr: references, relay URLs, and media.
content: The text content to render with
Nostr-specific formatting
video: Video elementsaudio: Audio elementsimage: Image elementsreference: References to other notes/usersurl: URL linksrelay: Relay URLsThis component does not support external templates.
Displays a search box and renders search results. Relies on NIP-50 queries to specific relays.
relays: Comma-separated list of relay URLslimit: Maximum number of results (default: 10)
placeholder: Placeholder text for search input
value: Initial search valuetemplate: ID of template element for custom
rendering
wrapper: Main containerinput: Search input fieldresults: Results containeritem: Individual result itempicture: User profile picturename: User display namenip05: NIP-05 identifierselected: Fired when an item is picked. Event
detail includes pubkey, npub and metadata object
Supports external templates through the template attribute.
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.
ref: Event reference (naddr format)container: Main containertitle: Event titletime: Event timedescription: Event descriptionbuttons: RSVP buttons containerbutton: Individual buttonaccepted: Accepted button statetentative: Tentative button statedeclined: Declined button statedeclined: Custom text for decline buttonrsvp-sent: Custom message shown after RSVP is
sent
This component does not support external templates.
Displays metadata and renders the actual video of a NIP-53 livestream.
ref: Livestream reference (naddr format)template: ID of template element for custom
rendering
error: Error message containerheader: Stream header sectionimage: Stream thumbnailtitle: Stream titlestatus: Stream statustime: Stream time informationsummary: Stream descriptionstats: Statistics sectioncurrent: Current viewers sectioncurrent-value: Current viewer countvideo: Video playerparticipants: Participants sectionerror-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 displaySupports external templates through the template attribute.
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.
ref: Note reference (nevent format)event: Raw event JSONrelays: Comma-separated list of relay URLstemplate: ID of template element for custom
rendering
container: The main containerheader: Note header sectionauthor-link: Author name/profile linkauthor-name: Author's display nameauthor-npub-short: Shortened npubparent-link: Link to parent notelink: Note permalinkcontent: Note contentreference: Referenced contenturl: URL linksimage: Imagesvideo: Video contentaudio: Audio contentrelay: Relay referencesSupports external templates through the template attribute.
Displays a Nostr event in JSON format.
ref: Event reference (nevent/naddr/note format)
pre: Container elementkey: JSON key stylingkind: Event kind valuecontent: Event contentid: Event IDpubkey: Public keysig: SignatureThis component does not support external templates.