Overview
React Together makes it incredibly easy to add real-time collaborative features to your React applications. With just a few hooks, you can synchronize state across multiple users, create live cursors, build real-time chat, and much more.
✨ Easy to Use Simple hooks that work like built-in React state
🚀 No Backend Required Built on Multisynq infrastructure - just add your API key
🎯 Real-time Sync Automatic synchronization across all connected users
⚡ Fast Setup Get collaborative features working in minutes
Quick Example
Here’s how easy it is to create a synchronized counter:
import { useStateTogether } from 'react-together'
export function SynchronizedCounter () {
const [ count , setCount ] = useStateTogether ( 'counter' , 0 )
return (
< button onClick = { () => setCount ( count + 1 ) } >
Count: { count }
</ button >
)
}
That’s it! This counter will stay synchronized across all users in real-time.
Installation
Install React Together using your preferred package manager:
npm install react-together
Basic Setup
1. Get Your API Key
Get API Key Sign up for free at multisynq.io to get your API key
2. Wrap Your App
Wrap your React app with the ReactTogether
provider:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { ReactTogether } from 'react-together'
import App from './App'
ReactDOM . createRoot ( document . getElementById ( 'root' ) ! ). render (
< ReactTogether
apiKey = "your_api_key_here"
appId = "com.example.myapp"
sessionParams = { {
name: "my-session" ,
password: "optional-password"
} }
>
< App />
</ ReactTogether >
)
3. Use React Together Hooks
Now you can use React Together hooks anywhere in your app:
import { useStateTogether , useConnectedUsers } from 'react-together'
function App () {
const [ message , setMessage ] = useStateTogether ( 'message' , 'Hello World!' )
const connectedUsers = useConnectedUsers ()
return (
< div >
< h1 > { message } </ h1 >
< input
value = { message }
onChange = { ( e ) => setMessage ( e . target . value ) }
placeholder = "Type a message..."
/>
< p > Connected users: { connectedUsers . length } </ p >
</ div >
)
}
Core Hooks
useStateTogether
Synchronize state across all users - works just like useState
:
import { useStateTogether } from 'react-together'
function SharedCounter () {
const [ count , setCount ] = useStateTogether ( 'counter' , 0 )
return (
< div >
< p > Count: { count } </ p >
< button onClick = { () => setCount ( count + 1 ) } > + </ button >
< button onClick = { () => setCount ( count - 1 ) } > - </ button >
< button onClick = { () => setCount ( 0 ) } > Reset </ button >
</ div >
)
}
useConnectedUsers
Get information about all connected users:
import { useConnectedUsers } from 'react-together'
function UserList () {
const users = useConnectedUsers ()
return (
< div >
< h3 > Connected Users ( { users . length } ) </ h3 >
< ul >
{ users . map ( user => (
< li key = { user . id } >
{ user . nickname || `User ${ user . id . slice ( 0 , 8 ) } ` }
</ li >
)) }
</ ul >
</ div >
)
}
useCursors
Display live mouse cursors for all users:
import { useCursors } from 'react-together'
function LiveCursors () {
const cursors = useCursors ()
return (
< div style = { { position: 'relative' , height: '100vh' } } >
{ /* Your app content */ }
{ /* Live cursors */ }
{ cursors . map ( cursor => (
< div
key = { cursor . userId }
style = { {
position: 'absolute' ,
left: cursor . x ,
top: cursor . y ,
pointerEvents: 'none' ,
backgroundColor: cursor . color ,
borderRadius: '50%' ,
width: 12 ,
height: 12 ,
transform: 'translate(-50%, -50%)'
} }
/>
)) }
</ div >
)
}
Configuration Options
ReactTogether Provider Props
Unique identifier for your app (e.g., “com.example.myapp”)
Session configuration options
Session name to join. If not provided, will auto-generate from URL parameters.
Optional session password for private rooms
Information about the current user
Display name for the current user
Color to represent this user (for cursors, etc.)
Example with All Options
< ReactTogether
apiKey = "your_api_key_here"
appId = "com.example.myapp"
sessionParams = { {
name: "room-123" ,
password: "secret"
} }
userInfo = { {
nickname: "John Doe" ,
color: "#ff6b6b"
} }
>
< App />
</ ReactTogether >
Complete Examples
Real-time Todo List
import { useStateTogether } from 'react-together'
import { useState } from 'react'
interface Todo {
id : string
text : string
completed : boolean
createdBy : string
}
function TodoApp () {
const [ todos , setTodos ] = useStateTogether < Todo []>( 'todos' , [])
const [ newTodo , setNewTodo ] = useState ( '' )
const addTodo = () => {
if ( newTodo . trim ()) {
const todo : Todo = {
id: crypto . randomUUID (),
text: newTodo ,
completed: false ,
createdBy: 'current-user' // In real app, get from user context
}
setTodos ([ ... todos , todo ])
setNewTodo ( '' )
}
}
const toggleTodo = ( id : string ) => {
setTodos ( todos . map ( todo =>
todo . id === id ? { ... todo , completed: ! todo . completed } : todo
))
}
const deleteTodo = ( id : string ) => {
setTodos ( todos . filter ( todo => todo . id !== id ))
}
return (
< div >
< h1 > Collaborative Todo List </ h1 >
< div >
< input
value = { newTodo }
onChange = { ( e ) => setNewTodo ( e . target . value ) }
onKeyPress = { ( e ) => e . key === 'Enter' && addTodo () }
placeholder = "Add a new todo..."
/>
< button onClick = { addTodo } > Add </ button >
</ div >
< ul >
{ todos . map ( todo => (
< li key = { todo . id } style = { {
textDecoration: todo . completed ? 'line-through' : 'none'
} } >
< input
type = "checkbox"
checked = { todo . completed }
onChange = { () => toggleTodo ( todo . id ) }
/>
< span > { todo . text } </ span >
< button onClick = { () => deleteTodo ( todo . id ) } > Delete </ button >
</ li >
)) }
</ ul >
</ div >
)
}
Live Drawing Canvas
import { useStateTogether , useCursors } from 'react-together'
import { useRef , useEffect } from 'react'
interface DrawPoint {
x : number
y : number
color : string
}
function DrawingCanvas () {
const canvasRef = useRef < HTMLCanvasElement >( null )
const [ strokes , setStrokes ] = useStateTogether < DrawPoint [][]>( 'strokes' , [])
const [ isDrawing , setIsDrawing ] = useState ( false )
const [ currentStroke , setCurrentStroke ] = useState < DrawPoint []>([])
const cursors = useCursors ()
const startDrawing = ( e : React . MouseEvent ) => {
setIsDrawing ( true )
const rect = canvasRef . current ?. getBoundingClientRect ()
if ( rect ) {
const point = {
x: e . clientX - rect . left ,
y: e . clientY - rect . top ,
color: '#000'
}
setCurrentStroke ([ point ])
}
}
const draw = ( e : React . MouseEvent ) => {
if ( ! isDrawing ) return
const rect = canvasRef . current ?. getBoundingClientRect ()
if ( rect ) {
const point = {
x: e . clientX - rect . left ,
y: e . clientY - rect . top ,
color: '#000'
}
setCurrentStroke ( prev => [ ... prev , point ])
}
}
const stopDrawing = () => {
if ( isDrawing && currentStroke . length > 0 ) {
setStrokes ( prev => [ ... prev , currentStroke ])
setCurrentStroke ([])
}
setIsDrawing ( false )
}
// Render canvas
useEffect (() => {
const canvas = canvasRef . current
const ctx = canvas ?. getContext ( '2d' )
if ( ! canvas || ! ctx ) return
ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
// Draw completed strokes
strokes . forEach ( stroke => {
if ( stroke . length > 1 ) {
ctx . beginPath ()
ctx . moveTo ( stroke [ 0 ]. x , stroke [ 0 ]. y )
stroke . forEach ( point => ctx . lineTo ( point . x , point . y ))
ctx . strokeStyle = stroke [ 0 ]. color
ctx . stroke ()
}
})
// Draw current stroke
if ( currentStroke . length > 1 ) {
ctx . beginPath ()
ctx . moveTo ( currentStroke [ 0 ]. x , currentStroke [ 0 ]. y )
currentStroke . forEach ( point => ctx . lineTo ( point . x , point . y ))
ctx . strokeStyle = '#000'
ctx . stroke ()
}
}, [ strokes , currentStroke ])
return (
< div style = { { position: 'relative' } } >
< canvas
ref = { canvasRef }
width = { 800 }
height = { 600 }
style = { { border: '1px solid #ccc' , cursor: 'crosshair' } }
onMouseDown = { startDrawing }
onMouseMove = { draw }
onMouseUp = { stopDrawing }
onMouseLeave = { stopDrawing }
/>
{ /* Live cursors */ }
{ cursors . map ( cursor => (
< div
key = { cursor . userId }
style = { {
position: 'absolute' ,
left: cursor . x ,
top: cursor . y ,
width: 10 ,
height: 10 ,
borderRadius: '50%' ,
backgroundColor: cursor . color || '#ff6b6b' ,
pointerEvents: 'none' ,
transform: 'translate(-50%, -50%)'
} }
/>
)) }
< button onClick = { () => setStrokes ([]) } >
Clear Canvas
</ button >
</ div >
)
}
Next Steps
Troubleshooting
Components not synchronizing
Verify your API key is correct
Check that all users are in the same session (same appId
and session name
)
Ensure the ReactTogether
provider wraps all components using hooks
Get your API key from multisynq.io/coder
Make sure you’re not hitting rate limits
Check your network connection
React Together is built with TypeScript and provides full type safety
Import types when needed: import type { CursorData } from 'react-together'
React Together makes building collaborative React apps incredibly simple. With just a few hooks, you can add real-time features that would normally require complex backend infrastructure. Get started today and build something amazing!
Responses are generated using AI and may contain mistakes.