A complete session management UI component for creating, joining, and leaving collaboration sessions with QR code sharing.
SessionManager
component provides a pre-built UI for managing collaboration sessions. It offers a floating button that opens a dialog with options to create new sessions, share join URLs with QR codes, and leave current sessions. This component is perfect for applications that need easy session management without custom UI development.
import { SessionManager } from 'react-together'
function MyApp() {
return (
<div className="app">
{/* Your main app content */}
<main>
<h1>My Collaborative App</h1>
<div className="content">
{/* App content goes here */}
</div>
</main>
{/* Session manager button - typically in a corner */}
<div className="fixed bottom-4 right-4">
<SessionManager />
</div>
</div>
)
}
SessionManager
component takes no props - it’s completely self-contained and manages its own state.
import { SessionManager, useIsTogether, useConnectedUsers } from 'react-together'
function CollaborativeWorkspace() {
const isTogether = useIsTogether()
const connectedUsers = useConnectedUsers()
return (
<div className="workspace">
<header className="workspace-header">
<h1>Team Workspace</h1>
<div className="connection-status">
{isTogether ? (
<span className="connected">
✅ Connected ({connectedUsers.length} users)
</span>
) : (
<span className="disconnected">
❌ Not connected
</span>
)}
</div>
</header>
<main className="workspace-content">
<div className="document-area">
<h2>Shared Document</h2>
<p>Start collaborating with your team...</p>
</div>
<div className="sidebar">
<h3>Team Members</h3>
<ul>
{connectedUsers.map(user => (
<li key={user.userId}>
{user.nickname || `User ${user.userId.slice(0, 8)}`}
</li>
))}
</ul>
</div>
</main>
{/* Session manager in bottom-right corner */}
<SessionManager />
</div>
)
}
import { SessionManager, useIsTogether, useJoinUrl } from 'react-together'
function Dashboard() {
const isTogether = useIsTogether()
const joinUrl = useJoinUrl()
return (
<div className="dashboard">
<nav className="dashboard-nav">
<h1>Project Dashboard</h1>
<div className="nav-actions">
{isTogether && (
<div className="session-info">
<span className="session-indicator">
🔗 Session Active
</span>
<span className="session-url">
{joinUrl ? `${joinUrl.slice(0, 30)}...` : 'Loading...'}
</span>
</div>
)}
</div>
</nav>
<div className="dashboard-content">
<div className="dashboard-cards">
<div className="card">
<h3>Tasks</h3>
<p>Collaborative task management</p>
</div>
<div className="card">
<h3>Files</h3>
<p>Shared file workspace</p>
</div>
<div className="card">
<h3>Chat</h3>
<p>Team communication</p>
</div>
</div>
</div>
{/* Session manager integrated into corner */}
<div className="session-manager-container">
<SessionManager />
</div>
</div>
)
}
import { SessionManager, useIsTogether, useStateTogether, useConnectedUsers } from 'react-together'
function DemoApp() {
const isTogether = useIsTogether()
const connectedUsers = useConnectedUsers()
const [demoState, setDemoState] = useStateTogether('demo-state', {
clicks: 0,
message: 'Hello World!'
})
return (
<div className="demo-app">
<div className="demo-header">
<h1>🚀 React Together Demo</h1>
<div className="demo-stats">
<div className="stat">
<label>Status:</label>
<span className={isTogether ? 'connected' : 'disconnected'}>
{isTogether ? '🟢 Connected' : '🔴 Disconnected'}
</span>
</div>
<div className="stat">
<label>Users:</label>
<span>{connectedUsers.length}</span>
</div>
</div>
</div>
<div className="demo-content">
<div className="demo-section">
<h2>Shared Counter</h2>
<div className="counter-display">
<span className="counter-value">{demoState.clicks}</span>
<button
onClick={() => setDemoState(prev => ({ ...prev, clicks: prev.clicks + 1 }))}
className="counter-button"
>
Click Me! (+1)
</button>
</div>
</div>
<div className="demo-section">
<h2>Shared Message</h2>
<input
type="text"
value={demoState.message}
onChange={(e) => setDemoState(prev => ({ ...prev, message: e.target.value }))}
placeholder="Type a shared message..."
className="demo-input"
/>
</div>
<div className="demo-section">
<h2>Connected Users</h2>
<div className="user-list">
{connectedUsers.map(user => (
<div key={user.userId} className="user-card">
<div className="user-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="user-name">
{user.nickname || `User ${user.userId.slice(0, 8)}`}
</span>
</div>
))}
</div>
</div>
</div>
{/* Session manager for easy demo sharing */}
<SessionManager />
</div>
)
}
import { SessionManager, useIsTogether } from 'react-together'
import { useState, useEffect } from 'react'
function MobileApp() {
const isTogether = useIsTogether()
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768)
}
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
return (
<div className="mobile-app">
<div className="mobile-header">
<h1>Mobile Collab</h1>
<div className="status-indicator">
{isTogether ? '🟢' : '🔴'}
</div>
</div>
<div className="mobile-content">
<div className="content-card">
<h2>Collaboration Space</h2>
<p>Share this session with your team using the session button below.</p>
{isMobile && (
<div className="mobile-tip">
💡 Tap the React Together button to share via QR code
</div>
)}
</div>
<div className="mobile-workspace">
<textarea
placeholder="Collaborative notes..."
className="mobile-textarea"
rows={8}
/>
</div>
</div>
{/* Position session manager for mobile */}
<div
className={`session-manager-mobile ${isMobile ? 'mobile' : 'desktop'}`}
style={{
position: 'fixed',
bottom: isMobile ? '20px' : '16px',
right: isMobile ? '20px' : '16px',
zIndex: 1000
}}
>
<SessionManager />
</div>
</div>
)
}
import { SessionManager, useIsTogether, useStateTogether, useConnectedUsers } from 'react-together'
function WorkshopApp() {
const isTogether = useIsTogether()
const connectedUsers = useConnectedUsers()
const [workshopState, setWorkshopState] = useStateTogether('workshop', {
currentSlide: 0,
questions: [] as string[],
polls: {} as Record<string, number>
})
const slides = [
{ title: "Welcome", content: "Welcome to our collaborative workshop!" },
{ title: "Introduction", content: "Let's learn together" },
{ title: "Interactive Exercise", content: "Everyone participate!" },
{ title: "Q&A", content: "Questions and answers" }
]
return (
<div className="workshop-app">
<div className="workshop-header">
<h1>📚 Collaborative Workshop</h1>
<div className="workshop-controls">
<div className="participant-count">
👥 {connectedUsers.length} participants
</div>
<div className="connection-status">
{isTogether ? '🟢 Live' : '🔴 Offline'}
</div>
</div>
</div>
<div className="workshop-content">
<div className="slide-area">
<div className="slide-header">
<h2>{slides[workshopState.currentSlide].title}</h2>
<div className="slide-navigation">
<button
onClick={() => setWorkshopState(prev => ({
...prev,
currentSlide: Math.max(0, prev.currentSlide - 1)
}))}
disabled={workshopState.currentSlide === 0}
>
← Previous
</button>
<span className="slide-counter">
{workshopState.currentSlide + 1} / {slides.length}
</span>
<button
onClick={() => setWorkshopState(prev => ({
...prev,
currentSlide: Math.min(slides.length - 1, prev.currentSlide + 1)
}))}
disabled={workshopState.currentSlide === slides.length - 1}
>
Next →
</button>
</div>
</div>
<div className="slide-content">
<p>{slides[workshopState.currentSlide].content}</p>
{workshopState.currentSlide === 2 && (
<div className="interactive-poll">
<h3>Quick Poll: How are you feeling?</h3>
<div className="poll-options">
{['😊 Great', '😐 Okay', '😴 Tired'].map(option => (
<button
key={option}
onClick={() => {
const currentVotes = workshopState.polls[option] || 0
setWorkshopState(prev => ({
...prev,
polls: {
...prev.polls,
[option]: currentVotes + 1
}
}))
}}
className="poll-button"
>
{option} ({workshopState.polls[option] || 0})
</button>
))}
</div>
</div>
)}
</div>
</div>
<div className="participant-panel">
<h3>Participants</h3>
<div className="participant-list">
{connectedUsers.map(user => (
<div key={user.userId} className="participant">
<div className="participant-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="participant-name">
{user.nickname || `Participant ${user.userId.slice(0, 6)}`}
</span>
</div>
))}
</div>
<div className="session-instructions">
<h4>Share Session</h4>
<p>Use the React Together button to share this workshop session with others.</p>
</div>
</div>
</div>
{/* Session manager for workshop sharing */}
<SessionManager />
</div>
)
}
import { SessionManager, useIsTogether, useStateTogether, useConnectedUsers } from 'react-together'
function GameLobby() {
const isTogether = useIsTogether()
const connectedUsers = useConnectedUsers()
const [gameState, setGameState] = useStateTogether('game-lobby', {
gameStarted: false,
players: [] as Array<{id: string, ready: boolean, score: number}>,
gameMode: 'classic'
})
const myUser = connectedUsers.find(u => u.userId === 'current-user-id') // This would be from useMyId()
const toggleReady = () => {
setGameState(prev => ({
...prev,
players: prev.players.map(p =>
p.id === myUser?.userId ? { ...p, ready: !p.ready } : p
)
}))
}
return (
<div className="game-lobby">
<div className="lobby-header">
<h1>🎮 Game Lobby</h1>
<div className="lobby-info">
<span className="player-count">
{connectedUsers.length} / 4 players
</span>
<span className="connection-status">
{isTogether ? '🟢 Connected' : '🔴 Disconnected'}
</span>
</div>
</div>
<div className="lobby-content">
<div className="player-section">
<h2>Players</h2>
<div className="player-grid">
{connectedUsers.map(user => {
const playerState = gameState.players.find(p => p.id === user.userId)
return (
<div key={user.userId} className="player-card">
<div className="player-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<div className="player-info">
<span className="player-name">
{user.nickname || `Player ${user.userId.slice(0, 6)}`}
</span>
<span className={`player-status ${playerState?.ready ? 'ready' : 'not-ready'}`}>
{playerState?.ready ? '✅ Ready' : '⏳ Not Ready'}
</span>
</div>
</div>
)
})}
</div>
</div>
<div className="game-controls">
<h2>Game Settings</h2>
<div className="game-options">
<div className="option-group">
<label>Game Mode:</label>
<select
value={gameState.gameMode}
onChange={(e) => setGameState(prev => ({
...prev,
gameMode: e.target.value
}))}
>
<option value="classic">Classic</option>
<option value="speed">Speed Mode</option>
<option value="team">Team Battle</option>
</select>
</div>
</div>
<div className="lobby-actions">
<button
onClick={toggleReady}
className={`ready-button ${gameState.players.find(p => p.id === myUser?.userId)?.ready ? 'ready' : ''}`}
>
{gameState.players.find(p => p.id === myUser?.userId)?.ready ? 'Not Ready' : 'Ready Up!'}
</button>
<button
onClick={() => setGameState(prev => ({ ...prev, gameStarted: true }))}
disabled={gameState.players.length < 2 || !gameState.players.every(p => p.ready)}
className="start-game-button"
>
Start Game
</button>
</div>
</div>
<div className="lobby-chat">
<h3>Lobby Chat</h3>
<div className="chat-placeholder">
<p>Chat integration would go here...</p>
</div>
</div>
</div>
{/* Session manager for inviting players */}
<SessionManager />
</div>
)
}
import { SessionManager, useIsTogether, useConnectedUsers } from 'react-together'
function VirtualEvent() {
const isTogether = useIsTogether()
const connectedUsers = useConnectedUsers()
return (
<div className="virtual-event">
<div className="event-header">
<h1>🎪 Virtual Event</h1>
<div className="event-info">
<span className="event-time">
📅 Today at 2:00 PM
</span>
<span className="attendee-count">
👥 {connectedUsers.length} attendees
</span>
</div>
</div>
<div className="event-content">
<div className="main-stage">
<div className="presentation-area">
<h2>Welcome to Our Virtual Event!</h2>
<p>Join the conversation and connect with other attendees.</p>
<div className="stage-controls">
<button className="stage-button">
🎤 Join Stage
</button>
<button className="stage-button">
💬 Open Chat
</button>
<button className="stage-button">
🙋 Raise Hand
</button>
</div>
</div>
</div>
<div className="attendee-panel">
<h3>Attendees</h3>
<div className="attendee-list">
{connectedUsers.map(user => (
<div key={user.userId} className="attendee">
<div className="attendee-avatar">
{user.nickname ? user.nickname[0].toUpperCase() : '?'}
</div>
<span className="attendee-name">
{user.nickname || `Attendee ${user.userId.slice(0, 6)}`}
</span>
<span className="attendee-status">
{isTogether ? '🟢' : '🔴'}
</span>
</div>
))}
</div>
<div className="event-sharing">
<h4>Invite Others</h4>
<p>Share this event with colleagues and friends using the session manager.</p>
</div>
</div>
</div>
{/* Session manager for event sharing */}
<SessionManager />
</div>
)
}
/* Session button */
.session-button {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 12px;
border-radius: 1rem;
color: white;
background-color: #3b82f6;
border: 1px solid #2d3748;
box-shadow: 0 2px 0 #2d3748;
}
/* Dialog components */
.sessionMenuContent-container { /* Dialog content */ }
.input-container { /* URL input area */ }
.qrCode-container { /* QR code display */ }
/* Custom button styling */
.session-button {
background-color: #your-brand-color !important;
border-color: #your-border-color !important;
}
/* Custom dialog styling */
.p-dialog .p-dialog-content {
background-color: #your-background-color;
}
/* Custom input styling */
.input-container {
background-color: #your-input-background;
border-color: #your-input-border;
}
// ✅ Good - Fixed positioning in corner
<div className="fixed bottom-4 right-4 z-50">
<SessionManager />
</div>
// ✅ Good - Integrated into layout
<div className="app-controls">
<SessionManager />
</div>
// ✅ Good - Responsive positioning
<div className={`session-manager ${isMobile ? 'mobile' : 'desktop'}`}>
<SessionManager />
</div>
// ✅ Good - Provide context
<div className="session-area">
<p>Use the React Together button to share this session</p>
<SessionManager />
</div>
ReactTogether
- Main context providerChat
- Real-time chat componentConnectedUsers
- User display componentuseCreateRandomSession
- Create new sessionsuseJoinUrl
- Get session join URLsuseLeaveSession
- Leave current sessionuseIsTogether
- Check connection statusimport { SessionManager } from 'react-together'
// No props needed - fully self-contained
function MyApp() {
return (
<div>
<SessionManager />
</div>
)
}