Skip to content

Step-by-Step Guide to Send a Nostr Message

This guide will walk you through sending a Nostr message in different environments using JavaScript. We’ll cover how to generate keys, create and sign an event, connect to relays, and publish the message to the Nostr network using WebSockets.

Step 1: Import the Nostr Tools Library

This tutorial requires the nostr-tools library to generate keys, create events, and interact with the Nostr network. We will use version 2.10.4 of the library. Here are the steps to import the library in different environments:

To import the Nostr Tools library in a browser environment, we need to first create an HTML file. Below is an HTML layout with a form to send a Nostr message with some basic styling.

<!DOCTYPE html>
<html>
<head>
<style>
#relays:not(:empty)::before {
content: "Published on:";
font-weight: bold;
}
</style>
</head>
<body>
<form id="nostr">
<input
name="note"
value="#hellonostr"
placeholder="Enter your message"
required
/>
<button type="submit">Send</button>
</form>
<ul id="keys"></ul>
<ul id="relays"></ul>
</body>
<script type="module">
// Snippets below will go here
</script>
</html>

Now, we can import the Nostr Tools library to generate keys and create events. We’ll use the esm.sh CDN to import it directly in the browser:

import * as NostrTools from "https://esm.sh/[email protected]";

Step 2: Define the Required Variables

Let’s define the required variables that we will use in the following steps.

In the web, we need to get the required elements and listen to the form submission event. We’ll also define the message content & list of relays where we want to publish the message.

const form = document.getElementById("nostr"),
keys = document.getElementById("keys"),
relays = document.getElementById("relays");
const relayUrls = [
"wss://relay.damus.io",
"wss://nostr.gleeze.com",
"wss://frysian.nostrich.casa",
"wss://nostr-01.yakihonne.com",
];
form.addEventListener("submit", async (event) => {
event.preventDefault();
const content = new FormData(form).get("note"); // Get the message content
// Snippets below will go here
});

Step 3: Key Generation

First, we generate a new private key, which can be in different formats: Uint8Array (byte array for raw data processing), hex (human-readable), or Bech32 (error-resistant, human-readable format). Here is the key generation process:

// Generate a new private key in Uint8Array format
const privKey = NostrTools.generateSecretKey();
// Get the public key in hex format from the private key
const pubKey = NostrTools.getPublicKey(privKey);
// Display private and public keys in Bech32 (nsec/npub) format
keys.innerHTML = `
<li><b>Private Key</b>: ${NostrTools.nip19.nsecEncode(privKey)}</li>
<li><b>Public Key: ${NostrTools.nip19.npubEncode(pubKey)}</li>
`;

Step 4: Create and Sign an Event

Next, we create a Kind 1 event (a note) and sign it with the private key:

// Create a new Nostr event
let NostrEvent = {
kind: 1, // Kind 1 is a text note
pubkey: pubKey,
created_at: Math.floor(Date.now() / 1000), // Unix timestamp in seconds
tags: [], // Array of references to other events ('e'), pubkeys ('p') or addressable content ('a')
content, // Your message content, defined in Step 2
};
// Sign the event with the private key
NostrEvent.id = NostrTools.getEventHash(NostrEvent);
NostrEvent = await NostrTools.finalizeEvent(NostrEvent, privKey);
// Log the signed event
console.log("Signed event:", NostrEvent);

Step 5: Connect to Relays and Send the Event

To broadcast the event to the Nostr network, we connect to relay servers via WebSockets. You can find a list of known relays at https://nostr.watch/. The relays handle the event in JSON format. Here’s how we send the event:

// Clear previous relay list
relays.innerHTML = "";
// Display the relays where the message was published
function displayRelays(contentHTML) {
relays.insertAdjacentHTML("beforeend", contentHTML);
}
relayUrls.forEach((url) => {
const socket = new WebSocket(url);
socket.onopen = () => {
console.log(`Connected to ${url}`);
// Send the signed event to the relay in JSON format
socket.send(JSON.stringify(["EVENT", NostrEvent]));
};
socket.onmessage = (message) => {
console.log(`Message from ${url}:`, message.data);
displayRelays(`<li>${url}</li>`); // Display relay URL
};
socket.onclose = () => {
console.log(`Disconnected from ${url}`);
};
});
// Clear the form
form.reset();

Additional Relay Information

  • Why use multiple relays? Using multiple relays increases the likelihood that your content will be distributed and stay accessible, even if some relays go offline. Relays operate independently, meaning they don’t automatically sync data with each other. Connecting to several relays ensures broader distribution of your messages and improves redundancy.

  • Free vs. paid relays: There are both free and paid relays in the Nostr ecosystem. Free relays are great for testing and learning, while paid relays typically offer better performance and reliability because they can afford to maintain higher-quality infrastructure.

  • Using relays based on follower lists: Some Nostr clients dynamically select relays based on the user’s follower list. This ensures that you’re connected to the same relays as the people you follow, improving message delivery and visibility.

Summary of the Process

To recap, here’s the process for sending a Nostr message:

  1. Key Generation: We generated a private/public key pair using Nostr tools.

  2. Creating and Signing an Event: We created a Kind 1 event (a note) and signed it with the private key.

  3. Connecting to Relays: We connected to multiple relay servers using WebSockets to ensure the message was distributed across the Nostr network.

  4. Displaying Published Relays: We displayed the relays where the message was successfully published.

By following this guide, you’ve successfully sent a Nostr message using HTML, JavaScript, and WebSockets. This provides a foundation for understanding how to interact with the Nostr protocol and work with its key features.