Overview
TheConnectedUsers component displays a visual representation of all users currently connected to your collaboration session. It shows user avatars in an attractive group layout with automatic overflow handling, making it perfect for headers, sidebars, or any area where you want to show team presence.
Perfect for: Application headers, team dashboards, collaboration toolbars, user presence indicators, and any interface where showing connected users improves the collaborative experience.
Basic Usage
import { ConnectedUsers } from 'react-together'
function AppHeader() {
return (
<header className="app-header">
<h1>My Collaborative App</h1>
<div className="user-presence">
<ConnectedUsers maxAvatars={5} />
</div>
</header>
)
}
Props
Maximum number of user avatars to display before showing a “+N” indicator
Enable debug mode to show additional user information (development only)
Examples
Header Integration
Display connected users in your application header:import { ConnectedUsers, useIsTogether } from 'react-together'
function ApplicationHeader() {
const isTogether = useIsTogether()
return (
<header className="app-header">
<div className="header-left">
<h1>Team Workspace</h1>
<div className="breadcrumb">
<span>Project</span>
<span>•</span>
<span>Document</span>
</div>
</div>
<div className="header-right">
<div className="collaboration-status">
{isTogether ? (
<div className="connected-section">
<span className="status-text">👥 Collaborating</span>
<ConnectedUsers maxAvatars={4} />
</div>
) : (
<div className="disconnected-section">
<span className="status-text">🔴 Offline</span>
</div>
)}
</div>
<div className="header-actions">
<button className="header-btn">Save</button>
<button className="header-btn">Share</button>
</div>
</div>
</header>
)
}
Sidebar User Panel
Create a detailed user panel with ConnectedUsers:import { ConnectedUsers, useConnectedUsers } from 'react-together'
function UserSidebar() {
const allUsers = useConnectedUsers()
return (
<div className="user-sidebar">
<div className="sidebar-header">
<h3>Team Members</h3>
<div className="user-count">
{allUsers.length} online
</div>
</div>
<div className="user-avatars">
<ConnectedUsers maxAvatars={6} />
</div>
<div className="user-list">
<h4>All Members</h4>
<div className="user-details">
{allUsers.map(user => (
<div key={user.userId} className="user-item">
<div className="user-avatar-small">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<div className="user-info">
<span className="user-name">
{user.nickname || `User ${user.userId.slice(0, 8)}`}
</span>
<span className="user-status">
🟢 Active
</span>
</div>
</div>
))}
</div>
</div>
</div>
)
}
Dashboard Overview
Use ConnectedUsers in a dashboard overview:import { ConnectedUsers, useConnectedUsers, useIsTogether } from 'react-together'
function Dashboard() {
const connectedUsers = useConnectedUsers()
const isTogether = useIsTogether()
return (
<div className="dashboard">
<div className="dashboard-header">
<h1>Project Dashboard</h1>
<div className="dashboard-stats">
<div className="stat-card">
<div className="stat-value">{connectedUsers.length}</div>
<div className="stat-label">Active Users</div>
</div>
<div className="stat-card">
<div className="stat-value">{isTogether ? '✅' : '❌'}</div>
<div className="stat-label">Connected</div>
</div>
</div>
</div>
<div className="dashboard-content">
<div className="content-section">
<div className="section-header">
<h2>Current Session</h2>
<div className="session-users">
<ConnectedUsers maxAvatars={8} />
</div>
</div>
<div className="session-activity">
<h3>Recent Activity</h3>
<div className="activity-list">
<div className="activity-item">
<span className="activity-time">2 min ago</span>
<span className="activity-text">User joined the session</span>
</div>
<div className="activity-item">
<span className="activity-time">5 min ago</span>
<span className="activity-text">Document was updated</span>
</div>
</div>
</div>
</div>
<div className="content-section">
<h2>Workspace</h2>
<div className="workspace-content">
<p>Collaborative workspace content...</p>
</div>
</div>
</div>
</div>
)
}
Floating User Indicator
Create a floating user presence indicator:import { ConnectedUsers, useConnectedUsers } from 'react-together'
import { useState } from 'react'
function FloatingUserIndicator() {
const connectedUsers = useConnectedUsers()
const [isExpanded, setIsExpanded] = useState(false)
return (
<div className="floating-user-indicator">
<div
className="indicator-trigger"
onClick={() => setIsExpanded(!isExpanded)}
>
<ConnectedUsers maxAvatars={3} />
<span className="user-count-badge">
{connectedUsers.length}
</span>
</div>
{isExpanded && (
<div className="indicator-popup">
<div className="popup-header">
<h4>Connected Users</h4>
<button onClick={() => setIsExpanded(false)}>
×
</button>
</div>
<div className="popup-content">
<div className="user-grid">
{connectedUsers.map(user => (
<div key={user.userId} className="user-card">
<div className="user-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<div className="user-details">
<div className="user-name">
{user.nickname || `User ${user.userId.slice(0, 8)}`}
</div>
<div className="user-status">
🟢 Online
</div>
</div>
</div>
))}
</div>
</div>
</div>
)}
</div>
)
}
Team Collaboration Toolbar
Integrate ConnectedUsers into a collaboration toolbar:import { ConnectedUsers, useConnectedUsers, useIsTogether } from 'react-together'
function CollaborationToolbar() {
const connectedUsers = useConnectedUsers()
const isTogether = useIsTogether()
return (
<div className="collaboration-toolbar">
<div className="toolbar-section">
<h3>Real-time Collaboration</h3>
<div className="collaboration-status">
{isTogether ? (
<div className="status-active">
<span className="status-indicator">🟢</span>
<span className="status-text">Live Session</span>
</div>
) : (
<div className="status-inactive">
<span className="status-indicator">🔴</span>
<span className="status-text">Offline</span>
</div>
)}
</div>
</div>
<div className="toolbar-section">
<div className="users-section">
<div className="users-header">
<span className="users-label">Team ({connectedUsers.length})</span>
</div>
<div className="users-display">
<ConnectedUsers maxAvatars={5} />
</div>
</div>
</div>
<div className="toolbar-section">
<div className="toolbar-actions">
<button className="toolbar-btn">
📝 Comments
</button>
<button className="toolbar-btn">
🔗 Share
</button>
<button className="toolbar-btn">
💬 Chat
</button>
</div>
</div>
</div>
)
}
Game Lobby Players
Show players in a game lobby:import { ConnectedUsers, useConnectedUsers, useStateTogether } from 'react-together'
function GameLobby() {
const connectedUsers = useConnectedUsers()
const [gameState, setGameState] = useStateTogether('game', {
maxPlayers: 4,
gameStarted: false
})
const canStartGame = connectedUsers.length >= 2 && connectedUsers.length <= gameState.maxPlayers
return (
<div className="game-lobby">
<div className="lobby-header">
<h1>🎮 Game Lobby</h1>
<div className="lobby-status">
<div className="player-count">
{connectedUsers.length} / {gameState.maxPlayers} players
</div>
<div className="lobby-state">
{gameState.gameStarted ? '🎯 In Game' : '⏳ Waiting'}
</div>
</div>
</div>
<div className="lobby-content">
<div className="players-section">
<h2>Players</h2>
<div className="players-display">
<ConnectedUsers maxAvatars={gameState.maxPlayers} />
</div>
<div className="player-slots">
{Array.from({ length: gameState.maxPlayers }).map((_, index) => {
const user = connectedUsers[index]
return (
<div
key={index}
className={`player-slot ${user ? 'occupied' : 'empty'}`}
>
{user ? (
<div className="player-info">
<div className="player-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="player-name">
{user.nickname || `Player ${user.userId.slice(0, 6)}`}
</span>
<span className="player-status">🟢 Ready</span>
</div>
) : (
<div className="empty-slot">
<div className="empty-avatar">?</div>
<span className="empty-text">Waiting for player...</span>
</div>
)}
</div>
)
})}
</div>
</div>
<div className="lobby-controls">
<button
onClick={() => setGameState(prev => ({ ...prev, gameStarted: true }))}
disabled={!canStartGame}
className="start-game-btn"
>
{canStartGame ? 'Start Game' : 'Need 2-4 players'}
</button>
</div>
</div>
</div>
)
}
Meeting Room Interface
Display meeting participants:import { ConnectedUsers, useConnectedUsers } from 'react-together'
import { useState } from 'react'
function MeetingRoom() {
const connectedUsers = useConnectedUsers()
const [isMuted, setIsMuted] = useState(false)
const [isVideoOn, setIsVideoOn] = useState(true)
return (
<div className="meeting-room">
<div className="meeting-header">
<h1>📹 Team Meeting</h1>
<div className="meeting-info">
<span className="meeting-time">
Started 15 minutes ago
</span>
<span className="participant-count">
{connectedUsers.length} participants
</span>
</div>
</div>
<div className="meeting-content">
<div className="video-grid">
<div className="main-video">
<div className="video-placeholder">
<h3>Main Screen</h3>
<p>Screen sharing or presentation view</p>
</div>
</div>
<div className="participant-videos">
{connectedUsers.slice(0, 6).map(user => (
<div key={user.userId} className="participant-video">
<div className="video-placeholder">
<div className="participant-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="participant-name">
{user.nickname || `User ${user.userId.slice(0, 6)}`}
</span>
</div>
</div>
))}
</div>
</div>
</div>
<div className="meeting-controls">
<div className="controls-left">
<button
onClick={() => setIsMuted(!isMuted)}
className={`control-btn ${isMuted ? 'muted' : ''}`}
>
{isMuted ? '🔇' : '🎤'}
</button>
<button
onClick={() => setIsVideoOn(!isVideoOn)}
className={`control-btn ${!isVideoOn ? 'video-off' : ''}`}
>
{isVideoOn ? '📹' : '📵'}
</button>
</div>
<div className="controls-center">
<div className="participants-indicator">
<ConnectedUsers maxAvatars={6} />
</div>
</div>
<div className="controls-right">
<button className="control-btn">
💬 Chat
</button>
<button className="control-btn">
🔗 Share
</button>
<button className="control-btn end-call">
📞 End Call
</button>
</div>
</div>
</div>
)
}
Responsive User Display
Adaptive user display for different screen sizes:import { ConnectedUsers, useConnectedUsers } from 'react-together'
import { useState, useEffect } from 'react'
function ResponsiveUserDisplay() {
const connectedUsers = useConnectedUsers()
const [screenSize, setScreenSize] = useState<'small' | 'medium' | 'large'>('large')
useEffect(() => {
const handleResize = () => {
const width = window.innerWidth
if (width < 768) {
setScreenSize('small')
} else if (width < 1024) {
setScreenSize('medium')
} else {
setScreenSize('large')
}
}
handleResize()
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
const getMaxAvatars = () => {
switch (screenSize) {
case 'small': return 2
case 'medium': return 4
case 'large': return 6
default: return 3
}
}
return (
<div className="responsive-user-display">
<div className="user-section">
<h3>
{screenSize === 'small' ? 'Team' : 'Team Members'}
</h3>
<div className="user-display">
<ConnectedUsers maxAvatars={getMaxAvatars()} />
</div>
{screenSize !== 'small' && (
<div className="user-details">
<span className="user-count">
{connectedUsers.length} member{connectedUsers.length !== 1 ? 's' : ''} online
</span>
</div>
)}
</div>
{screenSize === 'large' && (
<div className="detailed-user-list">
<h4>All Members</h4>
<div className="user-list">
{connectedUsers.map(user => (
<div key={user.userId} className="user-item">
<div className="user-avatar-small">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="user-name">
{user.nickname || `User ${user.userId.slice(0, 8)}`}
</span>
<span className="user-status">🟢</span>
</div>
))}
</div>
</div>
)}
</div>
)
}
Debug Mode
Use debug mode during development:import { ConnectedUsers } from 'react-together'
function DebugUserDisplay() {
return (
<div className="debug-container">
<h2>Debug Mode - User Display</h2>
<div className="debug-section">
<h3>Normal Display</h3>
<ConnectedUsers maxAvatars={4} />
</div>
<div className="debug-section">
<h3>Debug Mode (Development Only)</h3>
<ConnectedUsers maxAvatars={4} debug={true} />
</div>
</div>
)
}
Built-in Styling
The ConnectedUsers component uses PrimeReact’s AvatarGroup with these default styles:/* Avatar group container */
.p-avatar-group {
display: flex;
align-items: center;
gap: 10px;
}
/* Individual avatars */
.p-avatar {
background-color: #3b82f6;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
/* Overflow indicator */
.p-avatar.overflow {
background-color: #6b7280;
}
Customization
You can customize the appearance with CSS:/* Custom avatar colors */
.p-avatar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Custom avatar sizes */
.p-avatar.custom-size {
width: 48px;
height: 48px;
font-size: 16px;
}
/* Custom group spacing */
.p-avatar-group.custom-spacing {
gap: 5px;
}
/* Custom overflow indicator */
.p-avatar.overflow {
background-color: #f59e0b;
color: white;
}
Best Practices
Performance
// ✅ Good - Reasonable max avatars
<ConnectedUsers maxAvatars={6} />
// ❌ Bad - Too many avatars can clutter UI
<ConnectedUsers maxAvatars={20} />
User Experience
// ✅ Good - Clear context
<div className="team-section">
<h3>Team Members</h3>
<ConnectedUsers maxAvatars={5} />
</div>
// ✅ Good - Show user count
<div className="user-info">
<ConnectedUsers maxAvatars={4} />
<span>{connectedUsers.length} online</span>
</div>
Accessibility
// ✅ Good - Descriptive labels
<div className="user-presence" aria-label="Connected team members">
<ConnectedUsers maxAvatars={5} />
</div>
Mobile Optimization
// ✅ Good - Responsive avatar count
const maxAvatars = isMobile ? 3 : 6
<ConnectedUsers maxAvatars={maxAvatars} />
Common Patterns
- Header Integration: Show team presence in application headers
- Sidebar Display: Detailed user panels with additional information
- Floating Indicators: Expandable user presence indicators
- Gaming Lobbies: Player display in multiplayer games
- Meeting Interfaces: Participant display in virtual meetings
- Dashboard Cards: Team overview in admin dashboards
Related Components
SessionManager- Manage collaboration sessionsChat- Real-time team communicationHoverHighlighter- Show user interactions
Related Hooks
useConnectedUsers- The underlying hook powering ConnectedUsersuseMyId- Identify current useruseNicknames- Manage user display names
Dependencies
The ConnectedUsers component depends on:- PrimeReact: Avatar and AvatarGroup components
- React Together: User data and state management
TypeScript Support
The ConnectedUsers component is fully typed:import { ConnectedUsers } from 'react-together'
interface UserDisplayProps {
maxUsers?: number
showDebug?: boolean
}
function UserDisplay({ maxUsers = 4, showDebug = false }: UserDisplayProps) {
return (
<ConnectedUsers
maxAvatars={maxUsers}
debug={showDebug}
/>
)
}