{"id":19178035,"url":"https://github.com/cmdruid/use-nostr","last_synced_at":"2025-08-21T08:05:01.711Z","repository":{"id":168807357,"uuid":"641138064","full_name":"cmdruid/use-nostr","owner":"cmdruid","description":"A turn-key library for using Nostr with React.","archived":false,"fork":false,"pushed_at":"2023-06-12T17:37:21.000Z","size":179,"stargazers_count":19,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-07T20:43:21.257Z","etag":null,"topics":["client","hooks","nostr","react"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/@cmdcode/use-nostr","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cmdruid.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-05-15T21:27:15.000Z","updated_at":"2024-06-04T02:55:46.000Z","dependencies_parsed_at":"2023-09-07T22:08:21.492Z","dependency_job_id":null,"html_url":"https://github.com/cmdruid/use-nostr","commit_stats":null,"previous_names":["cmdruid/react-nostr-auth","cmdruid/use-nostr","cmdruid/usenostr"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmdruid%2Fuse-nostr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmdruid%2Fuse-nostr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmdruid%2Fuse-nostr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmdruid%2Fuse-nostr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cmdruid","download_url":"https://codeload.github.com/cmdruid/use-nostr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252954140,"owners_count":21830894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","hooks","nostr","react"],"created_at":"2024-11-09T10:36:18.004Z","updated_at":"2025-05-07T20:43:28.375Z","avatar_url":"https://github.com/cmdruid.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# useNostr\n\nThis project is designed to be a turn-key library for using Nostr with React.\n\n- Login with pubkey, npub, seckey, nsec, extension, or generate a new key.\n- Nostr client API with `get`, `list`, `sub`, and `publish`.\n- Configure a list of relays for your client to use.\n- Uses a simple reducer `store` with `update` method.\n- Helper boolean, status and error messages for reactive components.\n\n**NEW**: Nostr rooms now available! Easily create group messaging using a shared secret!\n\nComing soon:\n  - Sign / verify custom messages and challenges using the Signer API.\n  - Receive a Taproot HD wallet derived from your nostr signing device.\n  - Better integration of user profile and relay list.\n  - Remote signing support.\n\nThis project is fully typed and designed to work with intellisense.\n\nMore documentation coming soon!\n\n## Import\n\nTo make `useNostr` available across your entire react app, wrap your root component with the included `NostrProvider` component:\n\n```tsx\n// Example entrypoint for react / nextjs.\n// Your project may look slightly different.\nimport { NostrProvider } from '@cmdcode/use-nostr'\n\nexport default function App ({ Component, pageProps }) {\n  return (\n    \u003cNostrProvider\u003e\n      \u003cComponent {...pageProps} /\u003e\n    \u003c/NostrProvider\u003e\n  )\n}\n```\n\n## Basic Usage\n\nWith the `NostrProvider` configured, importing the library and store is relatively simple.\n\nHere is a basic example of reading and updating your relay list:\n\n```tsx\nimport { useState } from 'react'\nimport { useNostr } from '@cmdcode/use-nostr'\n\nexport default function Relays () {\n  const { store, update }   = useNostr()\n  const [ input, setInput ] = useState('')\n\n  function handleSubmit() {\n    update({ relays: [ ...store.relays, input ] })\n  }\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eRelays\u003c/p\u003e\n      { store.relays \u0026\u0026 \u003cpre\u003e{JSON.stringify(store.relays, null, 2)}\u003c/pre\u003e }\n      \u003cinput onChange={(e) =\u003e { setInput(e.target.value) }} value={input} /\u003e\n      \u003cbutton onClick={handleSubmit}\u003eAdd Relay\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nHere is an example login flow that covers all methods of signing in:\n\n```tsx\nimport { useState } from 'react'\nimport { useNostr } from '@cmdcode/use-nostr'\n\nexport default function Login () {\n  const [ pubkey, setPubKey ] = useState('')\n  const [ seckey, setSecKey ] = useState('')\n\n  const { hasExtension, login, store } = useNostr()\n\n  return (\n    \u003cdiv className='container'\u003e\n      \u003cdiv className='card'\u003e\n        \u003clabel\u003eLogin Via Extention:\u003c/label\u003e\n        \u003cbutton disabled={!hasExtension} onClick={login.withExt}\u003eLogin\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv className='card'\u003e\n        \u003clabel\u003eLogin with your Public Key or npub:\u003c/label\u003e\n        \u003cinput value={pubkey} onChange={(e) =\u003e setPubKey(e.target.value)}\u003e\u003c/input\u003e\n        \u003cbutton onClick={() =\u003e login.withPubKey(pubkey)}\u003eLogin\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv className='card'\u003e\n        \u003clabel\u003eLogin with your Secret Key or nsec:\u003c/label\u003e\n        \u003cinput value={seckey} onChange={(e) =\u003e setSecKey(e.target.value)}\u003e\u003c/input\u003e\n        \u003cdiv className='field'\u003e\n          \u003cbutton onClick={() =\u003e login.withSecKey(seckey)}\u003eLogin\u003c/button\u003e\n          \u003cbutton onClick={login.generateKey}\u003eGenerate\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n## Library API\n\nThe library is split into several modules that plug into the main store.\n\n### Store API\n\n```ts\nconst { store, update, reset, setError } = useNostr()\n\n// The main data store.\nstore : NostrStore\n// Update the data store using a JSON object.\nupdate   = (store : Partial\u003cNostrStore\u003e) =\u003e void\n// Resets the data store. Provide an \n// optional JSON object for defaults.\nreset    = (store : Partial\u003cNostrStore\u003e) =\u003e void\n// Sets an error message in the store, \n// plus print to console.\nsetError = (err : Error) =\u003e void\n\ninterface NostrStore {\n  // Schema for the data store.\n  client      ?: Client\n  connection   : 'none' | 'conn' | 'ok' | 'error'\n  hasExtension : boolean\n  isConnected  : boolean\n  isLoading    : boolean\n  error       ?: string\n  pubkey      ?: string\n  profile     ?: Profile\n  relays       : string[]\n  signer      ?: Signer\n}\n```\n\n### Login API\n\n```ts\nconst { login, logout } = useNostr()\n\nlogin = {\n  // Login with NIP-07 extension (if available).\n  withExt     : () =\u003e void,\n  // Login with npub or pubkey hex (read only).\n  withPubKey  : (string) =\u003e void,\n  // Login with nsec or seckey hex.\n  withSecKey  : (string) =\u003e void,\n  // Generate a new (ephemeral) keypair.\n  generateKey : () =\u003e void\n}\n\n// Removes all user data from the store.\nlogout = () =\u003e void\n```\n\n### Client API\n\n```ts\nconst { client } = useNostr()\n\nclient = {\n  // Checks pool connection and returns a boolean result.\n  connected : () =\u003e Promise\u003cboolean\u003e,\n  // Publish an event from a partial JSON template.\n  publish   : (event : Partial\u003cEvent\u003e) =\u003e Promise\u003cEvent\u003e,\n  // Fetch the top event using the provided filter.\n  get  : (filter  : Filter)   =\u003e Promise\u003cEvent\u003e,\n  // Fetch a list of events using the provided filters.\n  list : (filters : Filter[]) =\u003e Promise\u003cEvent[]\u003e,\n  // Publish a signed event and receive a Pub emitter.\n  pub  : (event   : Event)    =\u003e Promise\u003cPub\u003e,\n  // Subscribe to a list of filters and receive a Sub emitter.\n  sub  : (filters : Filter[]) =\u003e Promise\u003cSub\u003e\n}\n```\n\n### Profile API\n\n```ts\nconst { getProfile, setProfile } = useNostr()\n\n// Fetch your profile from the relays.\ngetProfile = () =\u003e Promise\u003cProfile | undefined\u003e\n// Update your profile using a partial JSON template.\nsetprofile = (updates : Partial\u003cProfile\u003e) =\u003e Promise\u003cvoid\u003e\n```\n\n### Room API\n\nRooms are a way to pass messages in real-time between a group of users. You can create or join a room using a secret string that is shared by all parties. All messages within the room are encrypted and covertly tagged using the shared secret.\n\nA room object works like a typical event emitter. You can broadcast a custom event to the room using `pub`, and listen for custom events using `on`, `once` or `within`. You can also emit events only to yourself using `emit`. Any callback methods registered to an event will receive a payload of data from the publisher, plus the event envelope it was wrapped in.\n\n```ts\nconst { joinRoom } = useNostr()\n\n// Multiple parties can join a single room using a secret string.\nconst room = joinRoom('secretstring')\n\n// You can publish any type of payload to any custom named event.\nroom.pub('customevent', { hello: 'world!' })\n\n// You can attach a callback method to any custom event.\nroom.on('customevent', (payload, envelope) =\u003e {\n  console.log(payload) // { hello: 'world!' }\n  // The envelope is the event object itself.\n  console.log(envelope)\n  /* {\n   *   \"kind\": 21111,\n   *   \"tags\": [ \n   *     [ \"h\", \"11ed64797736fdb7577bc10987e8b0a82210a93e90d39a217306e714504e97a4\" ],\n   *     [ \"expiration\", \"1772782615\" ]\n   *   ],\n   *   \"content\": \"6JArpHDMApqEY7dDuf_mo-KZHfMbZrZ6o6qJnXqxRuc8QzAWRz4zJ4kjzwCzM3Utf-_WeRJ-E59TCr3esj65gw?iv=MJjiOgQ7cGjAT5k6wWpHfg\",\n   *   \"created_at\": 1686382615\n   * }\n   */\n\nclass NostrRoom {\n  cache     : Array\u003cEventRecord\u003e // A rolling cache of past events.\n  config    : RoomConfig  // Configuration for the room.\n  events    : Callbacks   // List of callbacks registered for each event label.\n  connected : boolean     // Returns true once the room has an active subscription.\n  members   : string[]    // A list of pubkeys that represent active participants.\n  roomId    : Buff        // The identifer used to tag each event (for filtering).\n  sharedKey : Buff        // The shared key used for encryption (computed from secret).\n\n  // Emit an event only to yourself. Useful for internal logic.\n  emit (eventName: string, ...args: any[]) =\u003e void\n  // Broadcast an event to everyone in the room.\n  pub (\n    eventName : string,\n    payload   : Json,\n    template ?: Partial\u003cEventTemplate\u003e\n  ) =\u003e Promise\u003cEvent | undefined\u003e\n  // Register a callback method for a particular event.\n  on (eventName : string, fn : Function)   =\u003e void\n  // Register a callback that only executes once.\n  once (eventName : string, fn : Function) =\u003e void\n  // Register a callback that only exists for a limited time.\n  within (eventName : string, fn : Function, timeout: number) =\u003e void\n  // Remove a specific callback from the event list.\n  remove (eventName : string, fn : Function) =\u003e void\n  // Remove all callbacks registered to a specific event label.\n  prune(eventName: string) =\u003e void\n  // Unsubscribe the room from the relay pool.\n  leave() =\u003e void\n}\n\ntype EventRecord = [ eventName: string, payload: any, envelope: Event ]\ntype Callbacks   = Record\u003cstring, Set\u003cFunction\u003e\u003e\n\ninterface RoomConfig {\n  cacheSize      : number      // Sets the number of past events to store in cache.\n  allowEcho      : boolean     // Toggles receipt of events that you published yourself.\n  encryption     : boolean     // Toggles encryption for event content.\n  expiration     : number      // Events are set to expire after the time period.\n  filter         : Filter      // Customize the filter used to subscribe to room events.\n  inactiveLimit ?: number      // Users in the room are marked inactive after the time period.\n  kind           : number      // Set which kind number to use for each event envelope.\n  tags           : string[][]  // Set custom tags to be added to each event envelope.\n}\n```\n\n### Signer API\n\nComing soon!\n\n## Development / Testing\n\nThis library uses `yarn` for package management and `vite` for a development / demo server.\n\n```bash\n## Start the vite development server:\nyarn dev\n## Build a new release of the package:\nyarn release\n```\n\n## Bugs / Issues\n\nIf you run into any bugs or have any questions, please submit an issue ticket.\n\n## Contribution\n\nFeel free to fork and make contributions. Suggestions are welcome!\n\n## Dependencies\n\nThis library contains minimal dependencies.  \n\n**React**  \nReact library for hooks and JSX.  \nhttps://github.com/facebook/react\n\n**nostr-tools**  \nNostr utility library for all nostr stuff.  \nhttps://github.com/nbd-wtf/nostr-tools\n\n## Resources  \n\n**Nostr Implementation Possibilities**  \nThis site is an index of current and draft NIPs.  \nhttps://nips.be\n\n## License\n\nUse this library however you want!\n\n## Contact\n\nYou can find me on nostr at: `npub1gg5uy8cpqx4u8wj9yvlpwm5ht757vudmrzn8y27lwunt5f2ytlusklulq3`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmdruid%2Fuse-nostr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcmdruid%2Fuse-nostr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmdruid%2Fuse-nostr/lists"}