Access all user nicknames in the current React Together session for display and user identification
The useAllNicknames
hook returns an object containing all user nicknames in the current session. This is a read-only version of the useNicknames
hook, perfect when you only need to display nicknames without the ability to modify them.
If you need to read and modify nicknames, use useNicknames
instead.
import { useAllNicknames, useConnectedUsers } from 'react-together'
function UserList() {
const allNicknames = useAllNicknames()
const connectedUsers = useConnectedUsers()
return (
<div>
<h3>Connected Users</h3>
<ul>
{connectedUsers.map(({ userId, isYou }) => (
<li key={userId}>
{allNicknames[userId] || `User ${userId.slice(-4)}`}
{isYou && ' (You)'}
</li>
))}
</ul>
</div>
)
}
useAllNicknames(): Record<string, string>
An object mapping user IDs to their nicknames for all users in the current React Together session. Users without custom nicknames may not appear in this object.
Create a comprehensive user directory showing all participants:
import { useAllNicknames, useConnectedUsers } from 'react-together'
export default function UserDirectory() {
const allNicknames = useAllNicknames()
const connectedUsers = useConnectedUsers()
const sortedUsers = connectedUsers.sort((a, b) => {
// Sort with current user first, then alphabetically by nickname
if (a.isYou) return -1
if (b.isYou) return 1
const nicknameA = allNicknames[a.userId] || `User ${a.userId.slice(-4)}`
const nicknameB = allNicknames[b.userId] || `User ${b.userId.slice(-4)}`
return nicknameA.localeCompare(nicknameB)
})
return (
<div className="p-6 max-w-md mx-auto">
<h2 className="text-2xl font-bold mb-6 text-center">User Directory</h2>
<div className="space-y-3">
{sortedUsers.map(({ userId, isYou }) => {
const nickname = allNicknames[userId] || `User ${userId.slice(-4)}`
const avatarColor = `hsl(${userId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 360}, 70%, 50%)`
return (
<div
key={userId}
className={`flex items-center p-3 rounded-lg border-2 transition-all ${
isYou
? 'border-blue-300 bg-blue-50 shadow-md'
: 'border-gray-200 bg-white hover:border-gray-300'
}`}
>
{/* Avatar */}
<div
className="w-10 h-10 rounded-full flex items-center justify-center text-white font-bold mr-3"
style={{ backgroundColor: avatarColor }}
>
{nickname.charAt(0).toUpperCase()}
</div>
{/* User Info */}
<div className="flex-1">
<div className="font-semibold text-gray-900">
{nickname}
{isYou && <span className="ml-2 text-sm text-blue-600">(You)</span>}
</div>
<div className="text-sm text-gray-500">
ID: {userId.slice(-8)}
</div>
</div>
{/* Online Status */}
<div className="flex items-center">
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
<span className="ml-2 text-sm text-green-600">Online</span>
</div>
</div>
)
})}
</div>
<div className="mt-6 text-center text-sm text-gray-500">
{connectedUsers.length} user{connectedUsers.length !== 1 ? 's' : ''} online
</div>
</div>
)
}
Show user nicknames in a chat interface:
import { useAllNicknames, useChat } from 'react-together'
export default function ChatDisplay() {
const allNicknames = useAllNicknames()
const { messages } = useChat('chat-demo')
const formatTime = (timestamp: number) => {
return new Date(timestamp).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})
}
const getUserDisplayName = (userId: string) => {
return allNicknames[userId] || `User ${userId.slice(-4)}`
}
const getUserColor = (userId: string) => {
// Generate consistent color based on user ID
const hash = userId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
return `hsl(${hash % 360}, 70%, 45%)`
}
return (
<div className="p-4 h-96 bg-gray-50 rounded-lg">
<h3 className="text-lg font-semibold mb-4">Chat Messages</h3>
<div className="h-80 overflow-y-auto space-y-3">
{messages.length === 0 ? (
<div className="text-center text-gray-500 py-8">
No messages yet. Start the conversation!
</div>
) : (
messages.map(({ id, senderId, message, sentAt }) => (
<div key={id} className="flex items-start space-x-3">
{/* Avatar */}
<div
className="w-8 h-8 rounded-full flex items-center justify-center text-white font-bold text-sm flex-shrink-0"
style={{ backgroundColor: getUserColor(senderId) }}
>
{getUserDisplayName(senderId).charAt(0).toUpperCase()}
</div>
{/* Message Content */}
<div className="flex-1 min-w-0">
<div className="flex items-baseline space-x-2">
<span
className="font-semibold text-sm"
style={{ color: getUserColor(senderId) }}
>
{getUserDisplayName(senderId)}
</span>
<span className="text-xs text-gray-500">
{formatTime(sentAt)}
</span>
</div>
<p className="text-gray-900 text-sm mt-1 break-words">
{message}
</p>
</div>
</div>
))
)}
</div>
</div>
)
}
Track and display attendance for meetings or events:
import { useAllNicknames, useConnectedUsers, useStateTogether } from 'react-together'
import { useState, useEffect } from 'react'
interface AttendanceRecord {
userId: string
joinedAt: number
totalTime: number
isPresent: boolean
}
export default function AttendanceTracker() {
const allNicknames = useAllNicknames()
const connectedUsers = useConnectedUsers()
const [attendance, setAttendance] = useStateTogether<Record<string, AttendanceRecord>>('attendance', {})
const [sessionStart] = useState(Date.now())
// Update attendance when users join/leave
useEffect(() => {
const currentUserIds = new Set(connectedUsers.map(u => u.userId))
setAttendance(prev => {
const updated = { ...prev }
// Mark existing users as present/absent
Object.keys(updated).forEach(userId => {
const isPresent = currentUserIds.has(userId)
if (updated[userId].isPresent !== isPresent) {
updated[userId] = {
...updated[userId],
isPresent,
totalTime: isPresent
? updated[userId].totalTime
: updated[userId].totalTime + (Date.now() - updated[userId].joinedAt)
}
}
})
// Add new users
connectedUsers.forEach(({ userId }) => {
if (!updated[userId]) {
updated[userId] = {
userId,
joinedAt: Date.now(),
totalTime: 0,
isPresent: true
}
}
})
return updated
})
}, [connectedUsers, setAttendance])
const formatDuration = (ms: number) => {
const seconds = Math.floor(ms / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
if (hours > 0) {
return `${hours}h ${minutes % 60}m`
} else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`
} else {
return `${seconds}s`
}
}
const getCurrentSessionTime = (record: AttendanceRecord) => {
if (!record.isPresent) return record.totalTime
return record.totalTime + (Date.now() - record.joinedAt)
}
const sortedAttendance = Object.values(attendance).sort((a, b) => {
// Sort by presence (present first), then by total time
if (a.isPresent !== b.isPresent) {
return a.isPresent ? -1 : 1
}
return getCurrentSessionTime(b) - getCurrentSessionTime(a)
})
return (
<div className="p-6 max-w-2xl mx-auto">
<h2 className="text-2xl font-bold mb-6 text-center">Meeting Attendance</h2>
{/* Session Info */}
<div className="mb-6 p-4 bg-blue-50 rounded-lg">
<div className="text-center">
<div className="text-lg font-semibold text-blue-800">
Session Duration: {formatDuration(Date.now() - sessionStart)}
</div>
<div className="text-sm text-blue-600 mt-1">
{connectedUsers.length} currently present • {sortedAttendance.length} total participants
</div>
</div>
</div>
{/* Attendance List */}
<div className="space-y-3">
{sortedAttendance.length === 0 ? (
<div className="text-center text-gray-500 py-8">
No attendance records yet
</div>
) : (
sortedAttendance.map((record) => {
const nickname = allNicknames[record.userId] || `User ${record.userId.slice(-4)}`
const currentTime = getCurrentSessionTime(record)
return (
<div
key={record.userId}
className={`flex items-center justify-between p-4 rounded-lg border-2 ${
record.isPresent
? 'border-green-200 bg-green-50'
: 'border-gray-200 bg-gray-50'
}`}
>
<div className="flex items-center space-x-3">
{/* Status Indicator */}
<div className={`w-3 h-3 rounded-full ${
record.isPresent ? 'bg-green-500' : 'bg-gray-400'
}`} />
{/* User Info */}
<div>
<div className="font-semibold text-gray-900">
{nickname}
</div>
<div className="text-sm text-gray-500">
{record.isPresent ? 'Present' : 'Left meeting'}
</div>
</div>
</div>
{/* Time Info */}
<div className="text-right">
<div className="font-semibold text-gray-900">
{formatDuration(currentTime)}
</div>
<div className="text-xs text-gray-500">
{record.isPresent
? `Joined ${formatDuration(Date.now() - record.joinedAt)} ago`
: 'Disconnected'
}
</div>
</div>
</div>
)
})
)}
</div>
{/* Summary Stats */}
{sortedAttendance.length > 0 && (
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
<h3 className="font-semibold mb-2">Session Statistics</h3>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-600">Average session time:</span>
<div className="font-semibold">
{formatDuration(
sortedAttendance.reduce((sum, r) => sum + getCurrentSessionTime(r), 0) / sortedAttendance.length
)}
</div>
</div>
<div>
<span className="text-gray-600">Peak attendance:</span>
<div className="font-semibold">
{Math.max(connectedUsers.length, Object.values(attendance).filter(r => r.isPresent).length)}
</div>
</div>
</div>
</div>
)}
</div>
)
}
Show user rankings or scores using nicknames:
import { useAllNicknames, useStateTogetherWithPerUserValues } from 'react-together'
interface Score {
points: number
level: string
achievements: string[]
}
export default function Leaderboard() {
const allNicknames = useAllNicknames()
const [, , allScores] = useStateTogetherWithPerUserValues<Score>(
'game-scores',
{ points: 0, level: 'Beginner', achievements: [] }
)
const sortedPlayers = Object.entries(allScores)
.map(([userId, score]) => ({
userId,
nickname: allNicknames[userId] || `Player ${userId.slice(-4)}`,
...score
}))
.sort((a, b) => b.points - a.points)
const getRankEmoji = (index: number) => {
switch (index) {
case 0: return '🥇'
case 1: return '🥈'
case 2: return '🥉'
default: return `#${index + 1}`
}
}
const getLevelColor = (level: string) => {
const colors = {
'Beginner': 'bg-green-100 text-green-800',
'Intermediate': 'bg-blue-100 text-blue-800',
'Advanced': 'bg-purple-100 text-purple-800',
'Expert': 'bg-orange-100 text-orange-800',
'Master': 'bg-red-100 text-red-800'
}
return colors[level as keyof typeof colors] || 'bg-gray-100 text-gray-800'
}
return (
<div className="p-6 max-w-2xl mx-auto">
<h2 className="text-2xl font-bold mb-6 text-center">🏆 Leaderboard</h2>
{sortedPlayers.length === 0 ? (
<div className="text-center text-gray-500 py-8">
No players yet. Join the game to see the leaderboard!
</div>
) : (
<div className="space-y-3">
{sortedPlayers.map((player, index) => (
<div
key={player.userId}
className={`flex items-center p-4 rounded-lg border-2 transition-all ${
index === 0
? 'border-yellow-300 bg-yellow-50 shadow-lg'
: index <= 2
? 'border-gray-300 bg-gray-50 shadow-md'
: 'border-gray-200 bg-white'
}`}
>
{/* Rank */}
<div className="text-2xl font-bold mr-4 w-12 text-center">
{getRankEmoji(index)}
</div>
{/* Player Info */}
<div className="flex-1">
<div className="flex items-center space-x-3">
<h3 className="text-lg font-semibold text-gray-900">
{player.nickname}
</h3>
<span className={`px-2 py-1 text-xs font-medium rounded-full ${getLevelColor(player.level)}`}>
{player.level}
</span>
</div>
<div className="flex items-center space-x-4 mt-2">
<div className="text-sm text-gray-600">
<span className="font-semibold text-2xl text-gray-900">
{player.points.toLocaleString()}
</span> points
</div>
{player.achievements.length > 0 && (
<div className="flex items-center space-x-1">
<span className="text-sm text-gray-600">Achievements:</span>
<div className="flex space-x-1">
{player.achievements.slice(0, 3).map((achievement, i) => (
<span key={i} className="text-sm" title={achievement}>
🏆
</span>
))}
{player.achievements.length > 3 && (
<span className="text-xs text-gray-500">
+{player.achievements.length - 3}
</span>
)}
</div>
</div>
)}
</div>
</div>
{/* Position Change (mock data) */}
{index < sortedPlayers.length - 1 && (
<div className="text-right">
<div className="text-green-600 text-sm font-medium">
↗ +2
</div>
<div className="text-xs text-gray-500">
vs last week
</div>
</div>
)}
</div>
))}
</div>
)}
{/* Summary */}
{sortedPlayers.length > 0 && (
<div className="mt-6 p-4 bg-blue-50 rounded-lg text-center">
<div className="text-sm text-blue-800">
Total players: <span className="font-semibold">{sortedPlayers.length}</span>
{sortedPlayers.length > 0 && (
<>
<span className="mx-2">•</span>
Average score: <span className="font-semibold">
{Math.round(sortedPlayers.reduce((sum, p) => sum + p.points, 0) / sortedPlayers.length).toLocaleString()}
</span>
</>
)}
</div>
</div>
)}
</div>
)
}
Reference users by their display names in text:
const getUserName = (userId: string) => allNicknames[userId] || `User ${userId.slice(-4)}`
Display comprehensive user information:
connectedUsers.map(user => ({ ...user, displayName: allNicknames[user.userId] || 'Anonymous' }))
Show who sent messages in chat interfaces:
const senderName = allNicknames[message.senderId] || 'Unknown User'
useNicknames
- For reading and modifying nicknamesuseConnectedUsers
- Get information about connected usersuseMyId
- Get the current user’s IDThis hook returns a strongly-typed object:
const allNicknames: Record<string, string> = useAllNicknames()
// Safe access with optional chaining
const displayName = allNicknames[userId] ?? `User ${userId.slice(-4)}`
// Type-safe iteration
Object.entries(allNicknames).forEach(([userId, nickname]) => {
console.log(`${userId}: ${nickname}`)
})