NDK React Hooks
@nostr-dev-kit/ndk-hooks
provides a set of React hooks and utilities to seamlessly integrate Nostr functionality into your React applications using the Nostr Development Kit (NDK). This library simplifies managing Nostr data, user sessions, and event subscriptions within your React components.
Initialization
The core of ndk-hooks
revolves around a shared NDK instance. Initialize it once at the root of your application using the useNDKInit
hook. This ensures all hooks and stores use the same NDK configuration.
// components/ndk.tsx
'use client';
// Here we will initialize NDK and configure it to be available throughout the application
import NDK, { NDKNip07Signer, NDKPrivateKeySigner, NDKSigner } from "@nostr-dev-kit/ndk";
// An optional in-browser cache adapter
import NDKCacheAdapterDexie from "@nostr-dev-kit/ndk-cache-dexie";
import { NDKSessionLocalStorage, useNDKInit, useNDKSessionMonitor } from "@nostr-dev-kit/ndk-hooks";
import { useEffect } from "react";
// Define explicit relays or use defaults
const explicitRelayUrls = ["wss://relay.primal.net", "wss://nos.lol", "wss://purplepag.es"];
// Setup Dexie cache adapter (Client-side only)
let cacheAdapter: NDKCacheAdapterDexie | undefined;
if (typeof window !== "undefined") {
cacheAdapter = new NDKCacheAdapterDexie({ dbName: "your-app-name" });
}
// Create the singleton NDK instance
const ndk = new NDK({ explicitRelayUrls, cacheAdapter });
// Connect to relays on initialization (client-side)
if (typeof window !== "undefined") ndk.connect();
// Use the browser's localStorage for session storage
const sessionStorage = new NDKSessionLocalStorage();
/**
* Use an NDKHeadless component to initialize NDK in order to prevent application-rerenders
* when there are changes to the NDK or session state.
*
* Include this headless component in your app layout to initialize NDK correctly.
* @returns
*/
export default function NDKHeadless() {
const initNDK = useNDKInit();
useNDKSessionMonitor(sessionStorage, {
profile: true, // automatically fetch profile information for the active user
follows: true, // automatically fetch follows of the active user
});
useEffect(() => {
if (ndk) initNDK(ndk);
}, [initNDK])
return null;
}
// src/App.tsx
import React, { useEffect } from 'react';
import { useNDKInit } from '@nostr-dev-kit/ndk-hooks';
import { NDKSessionLocalStorage, useNDKSessionMonitor } from '@nostr-dev-kit/ndk-hooks';
import YourMainApp from './YourMainApp'; // Your main application component
import NDKHeadless from "components/ndk.tsx";
function App() {
return <ThemeProvider>
<NDKHeadless />
<YourMainApp />
</ThemeProvider>
}
export default App;
Once initialized, you can access the NDK instance anywhere in your component tree using the useNDK
hook if needed, although many hooks use it implicitly.
import { useNDK } from '@nostr-dev-kit/ndk-hooks';
function MyComponent() {
const { ndk } = useNDK();
// Use ndk instance...
}
Logging In
ndk-hooks
supports multiple-sessions. Use useNDKSessionLogin()
to add a session.
Sessions can be read-only (logging by providing an NDKUser
) or full-sessions with signing access (logging by providing an NDKSigner
).
import {useNDKSessionLogin, useNDKCurrentUser} from '@nostr-dev-kit/ndk-hooks';
import {NDKPrivateKeySigner} from '@nostr-dev-kit/ndk';
function Signin() {
const login = useNDKSessionLogin();
const nsec = "nsec1...."; // ask the user to enter their key or the preferred login method.
const currentUser = useNDKCurrentUser();
const handleLogin = useCallback(async () => {
const signer = new NDKPrivateKeySigner(nsec);
await login(signer)
aloert("hello!")
}, [])
useEffect(() => {
if (!currentUser) {
console.log('you are not logged in)
} else {
console.log('you are now logged in with user with pubkey', currentUser.pubkey)
}
}, [currentUser])
}
Fetching User Profiles
Easily fetch and display Nostr user profiles using the useProfileValue
hook. It handles caching and fetching logic automatically.
Using the useProfileValue
Hook
The useProfileValue
hook accepts two parameters:
pubkey
: The public key of the user whose profile you want to fetchoptions
: An optional object with the following properties:refresh
: A boolean indicating whether to force a refresh of the profilesubOpts
: NDKSubscriptionOptions to customize how the profile is fetched from relays
// Basic usage
const profile = useProfileValue(pubkey);
// Force refresh the profile
const profile = useProfileValue(pubkey, { refresh: true });
// With subscription options
const profile = useProfileValue(pubkey, {
refresh: false,
subOpts: {
closeOnEose: true,
// Other NDKSubscriptionOptions...
}
});
The hook returns the user's profile (NDKUserProfile) or undefined if the profile is not available yet.
// src/components/UserCard.tsx
import React from 'react';
import { useProfileValue } from '@nostr-dev-kit/ndk-hooks';
interface UserCardProps {
pubkey: string;
}
function UserCard({ pubkey }: UserCardProps) {
// Fetch profile with options
const profile = useProfileValue(pubkey, {
refresh: false, // Whether to force a refresh of the profile
subOpts: { /* NDKSubscriptionOptions */ } // Options for the subscription
});
if (!profile) {
return <div>Loading profile for {pubkey.substring(0, 8)}...</div>;
}
return (
<div>
<img
src={profile.picture || '/default-avatar.png'}
alt={profile.name || 'User Avatar'}
width={50}
height={50}
style={{ borderRadius: '50%' }}
/>
<h2>{profile.displayName || profile.name || 'Anonymous'}</h2>
<p>{profile.about}</p>
{profile.nip05 && <p>NIP-05: {profile.nip05}</p>}
</div>
);
}
export default UserCard;
Managing User Sessions (Login & Multi-Account)
ndk-hooks
provides robust session management, supporting both single and multiple user accounts. You can use the session monitoring functionality to automatically persist and restore user sessions across page reloads.
The session monitor will:
- Automatically restore sessions from storage when your app loads
- Persist new sessions when users log in
- Update storage when sessions change
- Remove sessions from storage when users log out
You can use this alongside the other session management hooks like useNDKSessionLogin
, useNDKSessionLogout
, and useNDKSessionSwitch
.
Subscribing to Events
Use the useSubscribe
hook for subscribing to Nostr events based on filters. It returns the events found in a stable set.
// src/components/NoteFeed.tsx
import React, { useState } from 'react';
import { NDKFilter, NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { useSubscribe } from '@nostr-dev-kit/ndk-hooks';
function NoteFeed() {
// there is no need to memoize filters
// Subscribe to events matching the filter
// `events` is a Set<NDKEvent> ordered by created_at (newest first by default)
const { events, eose } = useSubscribe(
[{ kinds: [NDKKind.Text] }], // no need to memoize filters, useSubscribe only depends on the explicit dependencies
{ /* in case you need to pass options for the subscription */ }, // NDKSubscriptionOptions
[...dependencies] // in case you need to change the subscription
);
return (
<div>
<h3>Recent Notes</h3>
{events.length === 0 && eose && <p>No notes found.</p>}
<ul>
{events.map((event: NDKEvent) => (
<li key={event.id}>
<p>{event.content}</p>
<small>By: <UserProfileLink pubkey={event.pubkey} /></small>
<small> At: {new Date(event.created_at! * 1000).toLocaleString()}</small>
</li>
))}
</ul>
</div>
);
}
// Helper component (replace with actual implementation)
function UserProfileLink({ pubkey }: { pubkey: string }) {
const profile = useProfileValue(pubkey, { refresh: true });
return <span>{profile?.name || pubkey.substring(0, 8)}</span>;
}
export default NoteFeed;
Fetching a Single Event with useEvent
The useEvent
hook allows you to fetch a single event by its ID or using a filter. This is useful when you need to retrieve and display a specific event, such as an article, note, or any other Nostr content.
// src/components/EventViewer.tsx
import React from 'react';
import { NDKEvent, NDKArticle } from '@nostr-dev-kit/ndk';
import { useEvent } from '@nostr-dev-kit/ndk-hooks';
function EventViewer({ eventId }: { eventId: string }) {
// Fetch a single event by ID or filter
// Returns undefined while loading, null if not found, or the event if found
const event = useEvent<NDKArticle>(
eventId,
{ wrap: true }, // Optional: UseSubscribeOptions
[] // Optional: dependencies array
);
if (event === undefined) return <div>Loading event...</div>;
if (event === null) return <div>Event not found</div>;
return (
<div>
<h2>{event.title || 'Untitled'}</h2>
<p>{event.content}</p>
<small>Published: {new Date(event.created_at! * 1000).toLocaleString()}</small>
</div>
);
}
export default EventViewer;
The useEvent
hook accepts three parameters:
idOrFilter
: A string ID, an NDKFilter object, or an array of NDKFilter objects to fetch the eventopts
: Optional UseSubscribeOptions to customize how the event is fetcheddependencies
: Optional array of dependencies that will trigger a refetch when changed
The hook returns:
undefined
while the event is being loadednull
if the event was not found- The event object if it was found
This makes it easy to handle loading states and display appropriate UI for each case.
Other Useful Hooks
ndk-hooks
provides several other specialized hooks:
useFollows()
: Fetches the follow list of the active user.useNDKWallet()
: Manages wallet connections (e.g., NWC) (viaimport of "@nostr-dev-kit/ndk-hooks/wallet"
)useNDKNutzapMonitor()
: Monitors for incoming zaps via Nutzap. (viaimport of "@nostr-dev-kit/ndk-hooks/wallet"
)
Muting
See Muting Documentation for details on how to mute, unmute, and check mute status for users, events, hashtags, and words using NDK React hooks.