Dark-theme peer-to-peer messaging MVP built with Next.js, TypeScript, Socket.IO, simple-peer, Zustand, IndexedDB, and Tailwind CSS.
The app behaves like a lightweight WhatsApp-style messenger, but users are identified by a generated Peer ID instead of a phone number. Messages are stored locally, presence is synced through a signaling server, and chat delivery prefers WebRTC with Socket.IO fallback.
- Automatic Peer ID generation and local persistence
- First-run display name prompt
- Live presence and last-active tracking
- Connection requests with approve/reject flow
- Real-time messaging over WebRTC with Socket.IO fallback
- Reply to message
- Delete message
- Delete conversation locally
- Local-first storage with IndexedDB
- Desktop and mobile responsive chat layout
- Copy Peer ID action and collapsible desktop sidebar
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS 4
- Zustand
- IndexedDB via
idb - Socket.IO / Socket.IO Client
- WebRTC via
simple-peer lru-cachelucide-react
app/
chat/
components/
hooks/
lib/
server/
store/
types/
docs/
Key entry points:
app/chat/page.tsx: main routecomponents/ChatApp.tsx: root client shellhooks/useMessages.ts: messaging, requests, reply/delete flowhooks/usePresence.ts: socket presence lifecyclelib/db.ts: IndexedDB persistenceserver/signalingServer.ts: signaling and relay server
Install dependencies:
npm installRun the Next.js app and signaling server together:
npm run dev:startOr run them separately:
npm run dev
npm run serverApp URL:
http://localhost:3000
Signaling server:
http://localhost:3001
npm run dev: start Next.js dev servernpm run server: start Socket.IO signaling server with nodemonnpm run dev:start: start frontend and signaling server togethernpm run lint: run ESLintnpm run build: production buildnpm run start: serve production build
- App opens and generates a Peer ID on first load.
- User is prompted for a display name.
- Client registers with the signaling server.
- Active users appear in the sidebar.
- User sends a connection request by Peer ID or from the available users list.
- Receiver approves or rejects.
- Approved chats can send messages in real time.
- Messages persist across refreshes through IndexedDB.
IndexedDB database name:
p2p-chat-db
Object stores:
userschatsmessages
Storage responsibilities:
- Users: presence, display name, timestamps
- Chats: summary metadata, unread count, approval state
- Messages: full local conversation history, replies, deletion markers, statuses
Transport strategy:
- Socket.IO is used for registration, presence, request flow, and signaling.
- WebRTC
simple-peeris used for peer-to-peer message delivery when available. - Socket.IO acts as the fallback relay when a peer connection is not ready.
For event-level details, see docs/realtime-events.md.
- Conversation deletion is local-only.
- Message deletion is propagated to the other peer.
- Connection requests are required before messaging.
- Display names are user-provided and stored locally.
The project has been verified with:
npm run lint
npx tsc --noEmit
npm run build