6.8 KiB
Project Overview
This project is a Nostr client application built with React 18.x, TailwindCSS 3.x, Vite, shadcn/ui, and Nostrify.
Technology Stack
- React 18.x: Stable version of React with hooks, concurrent rendering, and improved performance
- TailwindCSS 3.x: Utility-first CSS framework for styling
- Vite: Fast build tool and development server
- shadcn/ui: Unstyled, accessible UI components built with Radix UI and Tailwind
- Nostrify: Nostr protocol framework for Deno and web
- React Router: For client-side routing
- TanStack Query: For data fetching, caching, and state management
- TypeScript: For type-safe JavaScript development
Project Structure
/src/components/
: UI components including NostrProvider for Nostr integration/src/hooks/
: Custom hooks includinguseNostr
anduseNostrQuery
/src/pages/
: Page components used by React Router/src/lib/
: Utility functions and shared logic/public/
: Static assets
UI Components
The project uses shadcn/ui components located in @/components/ui
. These are unstyled, accessible components built with Radix UI and styled with Tailwind CSS. Available components include:
- Accordion: Vertically collapsing content panels
- Alert: Displays important messages to users
- AlertDialog: Modal dialog for critical actions requiring confirmation
- AspectRatio: Maintains consistent width-to-height ratio
- Avatar: User profile pictures with fallback support
- Badge: Small status descriptors for UI elements
- Breadcrumb: Navigation aid showing current location in hierarchy
- Button: Customizable button with multiple variants and sizes
- Calendar: Date picker component
- Card: Container with header, content, and footer sections
- Carousel: Slideshow for cycling through elements
- Chart: Data visualization component
- Checkbox: Selectable input element
- Collapsible: Toggle for showing/hiding content
- Command: Command palette for keyboard-first interfaces
- ContextMenu: Right-click menu component
- Dialog: Modal window overlay
- Drawer: Side-sliding panel
- DropdownMenu: Menu that appears from a trigger element
- Form: Form validation and submission handling
- HoverCard: Card that appears when hovering over an element
- InputOTP: One-time password input field
- Input: Text input field
- Label: Accessible form labels
- Menubar: Horizontal menu with dropdowns
- NavigationMenu: Accessible navigation component
- Pagination: Controls for navigating between pages
- Popover: Floating content triggered by a button
- Progress: Progress indicator
- RadioGroup: Group of radio inputs
- Resizable: Resizable panels and interfaces
- ScrollArea: Scrollable container with custom scrollbars
- Select: Dropdown selection component
- Separator: Visual divider between content
- Sheet: Side-anchored dialog component
- Sidebar: Navigation sidebar component
- Skeleton: Loading placeholder
- Slider: Input for selecting a value from a range
- Sonner: Toast notification manager
- Switch: Toggle switch control
- Table: Data table with headers and rows
- Tabs: Tabbed interface component
- Textarea: Multi-line text input
- Toast: Toast notification component
- ToggleGroup: Group of toggle buttons
- Toggle: Two-state button
- Tooltip: Informational text that appears on hover
These components follow a consistent pattern using React's forwardRef
and use the cn()
utility for class name merging. Many are built on Radix UI primitives for accessibility and customized with Tailwind CSS.
Nostr Protocol Integration
This project comes with custom hooks for querying and publishing events on the Nostr network.
The useNostr
Hook
The useNostr
hook returns an object containing a nostr
property, with .query()
and .event()
methods for querying and publishing Nostr events respectively.
import { useNostr } from '@nostrify/react';
function useCustomHook() {
const { nostr } = useNostr();
// ...
}
Query Nostr Data with useNostr
and Tanstack Query
When querying Nostr, the best practice is to create custom hooks that combine useNostr
and useQuery
to get the required data.
import { useNostr } from '@nostrify/react';
import { useQuery } from '@tanstack/query';
function usePosts() {
const { nostr } = useNostr();
return useQuery({
queryKey: ['posts'],
queryFn: async () => {
const events = await nostr.query([{ kinds: [1], limit: 20 }]);
return events; // these events could be transformed into another format
},
});
}
The data may be transformed into a more appropriate format if needed, and multiple calls to nostr.query()
may be made in a single queryFn.
The useNostrPublish
Hook
To publish events, use the useNostrPublish
hook in this project.
import { useState } from 'react';
import { useCurrentUser } from "@/hooks/useCurrentUser";
import { useNostrPublish } from '@/hooks/useNostrPublish';
export function MyComponent() {
const [ data, setData] = useState<Record<string, string>>({});
const { user } = useCurrentUser();
const { mutate: createEvent } = useNostrPublish();
const handleSubmit = () => {
createEvent({ kind: 1, content: data.content });
};
if (!user) {
return <span>You must be logged in to use this form.</span>;
}
return (
<form onSubmit={handleSubmit} disabled={!user}>
{/* ...some input fields */}
</form>
);
}
The useCurrentUser
hook should be used to ensure that the user is logged in before they are able to publish Nostr events.
Nostr Login
Nostr supports several types of logins:
- Login with nsec
- Login with browser extension
- Login with bunker URI
Functions to log in with each of these methods are exposed by the useLoginActions
hook:
function MyComponent() {
const login = useLoginActions();
login.nsec(nsec); // login by the user pasting their secret key
login.bunker(uri); // login by the user pasting a bunker URI
login.extension(); // login with a NIP-07 browser extension
return (
<div>{/* ... */}</div>
);
}
Development Practices
- Uses React Query for data fetching and caching
- Follows shadcn/ui component patterns
- Implements Path Aliases with
@/
prefix for cleaner imports - Uses Vite for fast development and production builds
- Component-based architecture with React hooks
- Default connection to multiple Nostr relays for network redundancy
Build & Deployment
- Build for production:
npm run build
- Development build:
npm run build:dev
Testing Your Changes
Whenever you modify code, you should test your changes after you're finished.
To test your changes, run npm run build
. The task is not considered finished until there are no build errors.