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.

Basic Usage

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>
  )
}

Signature

useAllNicknames(): Record<string, string>

Return Value

allNicknames
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.

Examples

User Directory

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>
  )
}

Chat User Display

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>
  )
}

Attendance Tracker

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>
  )
}

Leaderboard Display

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>
  )
}

Best Practices

Data Access

  • Combine with useConnectedUsers - Use together to get complete user information
  • Handle missing nicknames - Always provide fallback display names for users without custom nicknames
  • Cache locally - The hook automatically handles caching, but avoid unnecessary re-renders

Performance

  • Read-only access - This hook is optimized for reading nicknames only
  • Efficient filtering - When displaying large user lists, consider pagination or filtering
  • Memo components - Use React.memo for user list items to prevent unnecessary re-renders

User Experience

  • Consistent display - Use the same fallback pattern throughout your app
  • Visual hierarchy - Highlight current user or important users in lists
  • Accessibility - Provide proper ARIA labels and semantic markup

Common Patterns

User Mentions

Reference users by their display names in text:

const getUserName = (userId: string) => allNicknames[userId] || `User ${userId.slice(-4)}`

User Lists

Display comprehensive user information:

connectedUsers.map(user => ({ ...user, displayName: allNicknames[user.userId] || 'Anonymous' }))

Message Attribution

Show who sent messages in chat interfaces:

const senderName = allNicknames[message.senderId] || 'Unknown User'

TypeScript Support

This 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}`)
})