{"_id":"@amunrarara/nostr-hooks","name":"@amunrarara/nostr-hooks","dist-tags":{"latest":"2.6.3"},"versions":{"2.6.3":{"name":"@amunrarara/nostr-hooks","version":"2.6.3","description":"React hooks for developing Nostr clients.","main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","repository":{"type":"git","url":"git+https://github.com/amunrarara/nostr-hooks.git"},"funding":["https://getalby.com/p/sepehr"],"exports":{".":{"import":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc","test":"jest","format":"prettier --write .","prepare":"npm run build","prepublishOnly":"npm run build"},"jest":{"rootDir":".","preset":"ts-jest/presets/default-esm","testEnvironment":"jsdom","transform":{"^.+\\.(t|j)sx?$":["ts-jest",{"useESM":true}]},"extensionsToTreatAsEsm":[".ts",".tsx"],"moduleNameMapper":{"^nostr-hooks$":"<rootDir>/src/index.ts"},"modulePathIgnorePatterns":["dist"],"testRegex":"test.(js|ts|tsx)$","setupFilesAfterEnv":["<rootDir>/jest.setup.ts"]},"keywords":["nostr","decentralized","social","censorship-resistance","client","react","hooks"],"author":{"name":"Sepehr Safari"},"license":"MIT","devDependencies":{"@jest/globals":"^29.7.0","@testing-library/react":"^14.2.0","@types/lodash":"^4.14.202","@types/node":"^20.11.10","@types/react":"^18.2.48","@types/react-dom":"^18.2.18","@typescript-eslint/eslint-plugin":"^6.20.0","eslint":"^8.56.0","eslint-plugin-react":"^7.33.2","eslint-plugin-react-hooks":"^4.6.0","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","prettier":"^3.2.4","react":"^18.2.0","react-dom":"^18.2.0","ts-jest":"^29.1.2","tslib":"^2.6.2","typescript":"^5.4.5"},"dependencies":{"@nostr-dev-kit/ndk":"^2.8.1","@nostr-dev-kit/ndk-cache-dexie":"^2.4.1","lodash":"^4.17.21","zustand":"^4.5.2"},"_id":"@amunrarara/nostr-hooks@2.6.3","gitHead":"bba093365c272fab14f58b6cce822ac5e2292a2a","bugs":{"url":"https://github.com/amunrarara/nostr-hooks/issues"},"homepage":"https://github.com/amunrarara/nostr-hooks#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-zQ89Ef5KPsJ4QEWkSHwVstSyAVjjxnT4FF7j6ZDP1sELBjqTheoRhBjkMkRl3gHeb2n1YPS020fMdW74K3t8sw==","shasum":"bd11fd7664640140de219ceb8654f77ca2a89c39","tarball":"https://registry.npmjs.org/@amunrarara/nostr-hooks/-/nostr-hooks-2.6.3.tgz","fileCount":33,"unpackedSize":36707,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGHLj5pKDbvL7e0TbLGPFcFs55A5jcQHx6omfHm5RS+wIhANfiwezoI/kajS1Mm64DBGEIONBRwX3WbVsehEhi8cA9"}]},"_npmUser":{"name":"amunrarara","email":"npmjs.smell864@passmail.net"},"directories":{},"maintainers":[{"name":"amunrarara","email":"npmjs.smell864@passmail.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nostr-hooks_2.6.3_1714891392413_0.19440292940737458"},"_hasShrinkwrap":false}},"time":{"created":"2024-05-05T06:43:12.342Z","2.6.3":"2024-05-05T06:43:12.544Z","modified":"2024-05-05T06:43:12.789Z"},"maintainers":[{"name":"amunrarara","email":"npmjs.smell864@passmail.net"}],"description":"React hooks for developing Nostr clients.","homepage":"https://github.com/amunrarara/nostr-hooks#readme","keywords":["nostr","decentralized","social","censorship-resistance","client","react","hooks"],"repository":{"type":"git","url":"git+https://github.com/amunrarara/nostr-hooks.git"},"author":{"name":"Sepehr Safari"},"bugs":{"url":"https://github.com/amunrarara/nostr-hooks/issues"},"license":"MIT","readme":"![nostr-hooks](https://socialify.git.ci/ostyjs/nostr-hooks/image?description=1&descriptionEditable=React%20hooks%20for%20developing%20Nostr%20clients.&font=KoHo&forks=1&issues=1&language=1&name=1&owner=1&pattern=Charlie%20Brown&pulls=1&stargazers=1&theme=Dark)\n\n# Nostr-Hooks\n\nReact hooks for developing [Nostr](https://github.com/nostr-protocol/nostr) clients. It's simple yet intelligent.\n\n![NPM Downloads](https://img.shields.io/npm/dt/nostr-hooks)\n\nNostr-Hooks is a stateful wrapper library of React hooks around [NDK](https://github.com/nostr-dev-kit/ndk), designed to simplify the process of interacting with the Nostr protocol in real-time web applications. It provides an easy-to-use interface with low bandwidth consumption and high performance, allowing developers to quickly integrate Nostr into their projects and build responsive, real-time applications.\n\n## Migrate to v2.5\n\nI knew that replacing Zustand with React Context API was a bad idea. So, I decided to revert it back to Zustand. This means that you no longer need to wrap your application with the `NostrHooksContextProvider` component.\nNow you just need to initialize NostrHooks in your root component with just a simple hook.\n\n```jsx\nimport { useNostrHooks } from 'nostr-hooks';\n\nconst App = () => {\n  useNostrHooks();\n\n  return <YourApp />;\n};\n```\n\n## Migrate to v2\n\nNostr-Hooks v2 is a major release.\n\n- It replaces the `Zustand` store with the `React Context API`.\n  This means that now you need to wrap your application with the `NostrHooksContextProvider` component.\n\n- It replaces `nostr-tools` with `nostr-dev-kit (NDK)`.\n  This means that most of the functionalities like caching, batching, and merging filters are now handled by NDK and Nostr-Hooks is only responsible for managing the component state and subscriptions.\n\n## Installation\n\n```bash\nnpm install nostr-hooks\n```\n\n## Features\n\n- Provides a single instance of Nostr pool for the entire application, which is reused by all components.\n- Creates a single connection to each Nostr relay at a time and reuses it for all subscriptions, reducing network overhead.\n- Automatically manages subscriptions from multiple components and delivers only the events that each component needs.\n- Automatically batches multiple subscriptions from different components into a single subscription request, further reducing network overhead.\n- Intelligently merges filters into a unique set of filters, reducing the load on the Nostr relays.\n- Provides a built-in cache mechanism since version 1.1.\n- Minimizes re-renders by updating only the events that have changed, improving application performance.\n- Automatically cleans up subscriptions and garbage events when a component unmounts, preventing memory leaks.\n\n## Isn't NDK enough? Why do we need Nostr-Hooks?\n\n> Nostr-Hooks is not a replacement for NDK. You may still need to install NDK and use it in your application.\n\nNDK is a powerful library (shout-out to [pablo](https://github.com/pablof7z)) with a lot of out-of-the-box features, like caching, batching, and merging filters. However, it's a stateless library and doesn't understand the React component lifecycle. This means that it's up to the developer to update the component state when new events arrive, and to unsubscribe from the subscription when the component unmounts. This can be a tedious and error-prone process, especially when scaling the application. Nostr-Hooks on the other hand, is a stateful wrapper library that manages the component state and subscriptions automatically, allowing the developer to focus on building and scaling the application.\n\n## Usage\n\n### Initialize NostrHooks\n\nYou need to initialize NostrHooks in your root component in order to execute `ndk.connect()` automatically and create a single instance of Nostr pool for the entire application.\n\n```jsx\nimport { useNostrHooks } from 'nostr-hooks';\n\nconst App = () => {\n  useNostrHooks();\n\n  return <YourApp />;\n};\n```\n\n> You can also pass a custom NDK instance to the `useNostrHooks` hook. This is useful when you want to initiate your app with a custom NDK instance with your own configuration. You can also use other provided hooks like `useNdk` to interact with the NDK instance later.\n\n### Subscribe to events\n\nHere are some examples of how to use the `useSubscribe` hook:\n\n#### Example 1: Basic usage:\n\n```jsx\nimport { useSubscribe } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  const { events } = useSubscribe({\n    filters: [{ authors: ['pubkey1'], kinds: [1] }],\n  });\n\n  if (!events) return <p>Loading...</p>;\n\n  return (\n    <ul>\n      {events.map((event) => (\n        <li key={event.id}>\n          <p>{event.pubkey}</p>\n          <p>{event.kind}</p>\n        </li>\n      ))}\n    </ul>\n  );\n};\n```\n\nThe `useSubscribe` hook takes an object with one mandatory and two optional parameters:\n\n- `filters`: A mandatory array of filters that the subscription should be created for.\n- `enabled`: An optional boolean flag indicating whether the subscription is enabled. If set to `false`, the subscription will not be created automatically.\n- `opts`: An optional \"NDK Subscription Options\" object.\n- `relays`: An optional array of relay urls to use for the subscription. If not provided, the default relays will be used.\n- `fetchProfiles`: An optional boolean flag indicating whether to fetch profiles for the events in the subscription. If set to `true`, the profiles will be fetched automatically.\n\n> There are lots of options available for creating a subscription. [Read more about the NDK subscription options here](https://github.com/nostr-dev-kit/ndk)\n\nThe `useSubscribe` hook returns an object with four properties:\n\n- `events`: An array of events that match the filters.\n- `eose`: A boolean flag indicating whether the subscription has reached the end of the stream.\n- `unSubscribe`: A function that can be used to unsubscribe from the subscription.\n- `isSubscribed`: A boolean flag indicating whether the subscription is active.\n\n#### Example 2: Using multiple subscriptions in a single component:\n\n```jsx\nimport { useSubscribe } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  const { events: articles } = useSubscribe({\n    filters: [{ authors: ['pubkey'], kinds: [30023] }],\n  });\n\n  const { events: notes } = useSubscribe({\n    filters: [{ authors: ['pubkey'], kinds: [1] }],\n  });\n\n  return (\n    <>\n      <ul>\n        {articles.map((article) => (\n          <li key={article.id}>\n            <p>{article.pubkey}</p>\n            <p>{article.content}</p>\n          </li>\n        ))}\n      </ul>\n\n      <ul>\n        {notes.map((note) => (\n          <li key={note.id}>\n            <p>{note.pubkey}</p>\n            <p>{note.content}</p>\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n};\n```\n\nThe `useSubscribe` hook can be used multiple times in a single component. Nostr-Hooks batches all subscriptions into a single subscription request, and delivers only the events that each hook needs.\n\n#### Example 3: Using subscriptions in multiple components:\n\n```jsx\nimport { useSubscribe } from 'nostr-hooks';\n\nconst App = () => {\n  return (\n    <>\n      <ComponentA />\n      <ComponentB />\n    </>\n  );\n};\n\nconst ComponentA = () => {\n  const { events } = useSubscribe({\n    filters: [{ authors: ['pubkey'], kinds: [1] }],\n  });\n\n  return (\n    <ul>\n      {events.map((event) => (\n        <li key={event.id}>\n          <p>{event.pubkey}</p>\n          <p>{event.kind}</p>\n        </li>\n      ))}\n    </ul>\n  );\n};\n\nconst ComponentB = () => {\n  const { events } = useSubscribe({\n    filters: [{ authors: ['pubkey'], kinds: [30023] }],\n  });\n\n  return (\n    <ul>\n      {events.map((event) => (\n        <li key={event.id}>\n          <p>{event.pubkey}</p>\n          <p>{event.content}</p>\n        </li>\n      ))}\n    </ul>\n  );\n};\n```\n\nThe `useSubscribe` hook can be used in multiple components. Nostr-Hooks batches all subscriptions from all components into a single subscription request, and delivers only the events that each component needs.\n\n#### Example 4: Dependent subscriptions:\n\n```jsx\nimport { useSubscribe } from 'nostr-hooks';\n\nconst MyComponent = ({ noteId }: Params) => {\n  const { events } = useSubscribe({\n    filters: [{ ids: [noteId] }],\n    enabled: !!noteId,\n  });\n\n  return (\n    <>\n      <ul>\n        {events.map((event) => (\n          <li key={event.id}>\n            <p>{event.pubkey}</p>\n            <p>{event.content}</p>\n          </li>\n        ))}\n      </ul>\n    </>\n  );\n};\n```\n\nThe `useSubscribe` hook can be used in a component that depends on a prop or state. In this example, the subscription waits for the `noteId` prop to be set before creating the subscription.\n\n### Publish new events\n\nThe `useNewEvent` hook is used to create a new NDK event, which can then be published using the internal `publish` method.\n\n#### Example:\n\n```jsx\nimport { useNewEvent } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  const [content, setContent] = useState('');\n\n  const { createNewEvent } = useNewEvent();\n\n  const handlePublish = () => {\n    const event = createNewEvent();\n    event.content = content;\n    event.kind = 1;\n\n    event.publish();\n  };\n\n  return (\n    <>\n      <input type=\"text\" value={content} onChange={(e) => setContent(e.target.value)} />\n\n      <button onClick={() => handlePublish()}>Publish Note</button>\n    </>\n  );\n};\n```\n\n> There is also a `usePublish` hook that can be used to publish an existing NDK event.\n\n### Fetch Profiles\n\nThe `useProfiles` hook is used to fetch profiles for a given set of events or users.\n\n> The default behavior is to mutate the original events or users with the fetched profiles. To prevent this, you can use the `mutateOrignal` option and set it to `false`. In this case, the updated events or users will be returned from the `useProfiles` hook, and you can use them to render the UI.\n\n#### Example: Fetch profiles for a set of events:\n\nConsider a scenario where you have a list of events, and you want to fetch the profiles of the authors of those events.\n\n```jsx\nconst MyComponent = () => {\n  const { events } = useSubscribe({ filters });\n\n  useProfiles({ events });\n\n  return (\n    <ul>\n      {events.map((event) => (\n        <li key={event.id}>\n          <p>{event.author.profile?.name}</p>\n          <p>{event.author.profile?.bio}</p>\n        </li>\n      ))}\n    </ul>\n  );\n};\n```\n\nThe `useProfiles` hook will automatically fetch the profiles of the authors of the events, and mutate the original events with the fetched profiles. This means that the `author` property of each event will be updated with the fetched profile.\n\n### Interact with NDK instance\n\nYou can leverage `useNdk` hook to interact with the NDK instance. it returns the NDK instance itself, and two setter functions for updating the NDK instance and the NDK signer.\n\n```jsx\nimport { useNdk } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  const { ndk, setNdk, setSigner } = useNdk();\n\n  const handleUpdateNdk = ({ ndk }: NDK) => {\n    setNdk(new NDK({ /* ... */ })); // this will replace the existing NDK instance with the new one\n  };\n\n  const handleUpdateNdkSigner = ({ signer }: NDKSigner) => {\n    setSigner(new NDKNip07Signer()); // this will keep the existing NDK instance and update its signer\n  };\n};\n```\n\n### Getting the Active User Profile\n\nYou can use the `useActiveUser` hook to get the active user's profile based on the current NDK instance and its signer.\n\n```jsx\nimport { useActiveUser } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  const { activeUser } = useActiveUser();\n\n  return (\n    <div>\n      <p>{activeUser?.profile?.name}</p>\n    </div>\n  );\n};\n```\n\n### Using a NIP-07 browser extension (e.g. Alby, nos2x)\n\nYou can use the `useNip07` hook to update the current NDK instance with the NIP-07 browser extension's signer.\nThis hook will automatically update the `existing NDK instance` with the signer from the NIP-07 browser extension, and will prompt the user to connect to the NIP-07 browser extension if they haven't already.\n\n```jsx\nimport { useNip07 } from 'nostr-hooks';\n\nconst MyComponent = () => {\n  useNip07();\n\n  // ...\n};\n```\n\n> You can use this hook in the root component of your application for the entire application, or you can use it in a specific component where you need the user pubkey. This will update the NDK instance in the entire application.\n\n## Contributing\n\nWe welcome contributions from the community! If you'd like to contribute to Nostr-Hooks, please refer to the [CONTRIBUTING.md](https://github.com/ostyjs/nostr-hooks/blob/master/CONTRIBUTING.md) file in the project's GitHub repository.\n\n> You can also consider contributing to [NDK](https://github.com/nostr-dev-kit/ndk).\n\n## Donations\n\nIf you'd like to support the development of Nostr-Hooks, please consider donating to the developer.\n\n- ⚡ Zap sats to [sepehr@getalby.com](sepehr@getalby.com)\n\n> You can also consider supporting the [NDK](https://github.com/nostr-dev-kit/ndk).\n\n## License\n\nNostr-Hooks is licensed under the MIT License. For more information, see the [LICENSE.md](https://github.com/ostyjs/nostr-hooks/blob/master/LICENSE.md) file in the project's GitHub repository.\n\n## Contact\n\nIf you have any questions or concerns about Nostr-Hooks, please contact the developer at [npub18c556t7n8xa3df2q82rwxejfglw5przds7sqvefylzjh8tjne28qld0we7](https://njump.me/npub18c556t7n8xa3df2q82rwxejfglw5przds7sqvefylzjh8tjne28qld0we7).\n","readmeFilename":"README.md"}