Overview
useStateTogether
is the core hook of React Together. It creates state that is automatically synchronized across all users in your session. It works exactly like React’s useState
, but any state changes are instantly shared with all connected users.
Perfect for : Shared counters, form data, game state, collaborative editing, or any state that should be the same for all users.
Basic Usage
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 >
</ div >
)
}
Signature
useStateTogether < T >(
key : string ,
initialValue : T ,
options ?: UseStateTogetherOptions
): [ T , Dispatch < SetStateAction < T >> ]
Parameters
Unique identifier for this shared state. All users with the same key will share the same state.
Initial value when the state is first created. This only applies when no user has set this state yet.
Configuration options for the shared state behavior
Options
options.resetOnDisconnect
Whether to reset to initial value when all users disconnect
Milliseconds to throttle state updates (prevents excessive network traffic)
Return Value
Returns a tuple identical to useState
:
The current state value, synchronized across all users
[1]
Dispatch<SetStateAction<T>>
Function to update the state. Accepts new value or updater function.
Examples
Simple Counter
function Counter () {
const [ count , setCount ] = useStateTogether ( 'counter' , 0 )
return (
< div >
< h2 > Shared Counter: { count } </ h2 >
< button onClick = { () => setCount ( c => c + 1 ) } > Increment </ button >
< button onClick = { () => setCount ( 0 ) } > Reset </ button >
</ div >
)
}
Shared Text Input
function SharedTextEditor () {
const [ text , setText ] = useStateTogether ( 'document' , '' )
return (
< div >
< h3 > Collaborative Document </ h3 >
< textarea
value = { text }
onChange = { ( e ) => setText ( e . target . value ) }
placeholder = "Start typing... others will see your changes in real-time!"
rows = { 10 }
cols = { 50 }
/>
< p > Characters: { text . length } </ p >
</ div >
)
}
Complex Object State
interface UserPreferences {
theme : 'light' | 'dark'
fontSize : number
showNotifications : boolean
}
function SharedSettings () {
const [ preferences , setPreferences ] = useStateTogether < UserPreferences >(
'preferences' ,
{
theme: 'light' ,
fontSize: 14 ,
showNotifications: true
}
)
const updateTheme = ( theme : 'light' | 'dark' ) => {
setPreferences ( prev => ({ ... prev , theme }))
}
const updateFontSize = ( fontSize : number ) => {
setPreferences ( prev => ({ ... prev , fontSize }))
}
return (
< div style = { {
backgroundColor: preferences . theme === 'dark' ? '#333' : '#fff' ,
color: preferences . theme === 'dark' ? '#fff' : '#000' ,
fontSize: preferences . fontSize
} } >
< h3 > Shared Settings </ h3 >
< div >
< label > Theme: </ label >
< select
value = { preferences . theme }
onChange = { ( e ) => updateTheme ( e . target . value as 'light' | 'dark' ) }
>
< option value = "light" > Light </ option >
< option value = "dark" > Dark </ option >
</ select >
</ div >
< div >
< label > Font Size: </ label >
< input
type = "range"
min = "10"
max = "24"
value = { preferences . fontSize }
onChange = { ( e ) => updateFontSize ( parseInt ( e . target . value )) }
/>
< span > { preferences . fontSize } px </ span >
</ div >
< div >
< label >
< input
type = "checkbox"
checked = { preferences . showNotifications }
onChange = { ( e ) => setPreferences ( prev => ({
... prev ,
showNotifications: e . target . checked
})) }
/>
Show Notifications
</ label >
</ div >
</ div >
)
}
Array State Management
interface TodoItem {
id : string
text : string
completed : boolean
createdBy : string
}
function CollaborativeTodoList () {
const [ todos , setTodos ] = useStateTogether < TodoItem []>( 'todos' , [])
const [ newTodo , setNewTodo ] = useState ( '' )
const addTodo = () => {
if ( newTodo . trim ()) {
const todo : TodoItem = {
id: crypto . randomUUID (),
text: newTodo . trim (),
completed: false ,
createdBy: 'current-user' // In real app, get from user context
}
setTodos ( prevTodos => [ ... prevTodos , todo ])
setNewTodo ( '' )
}
}
const toggleTodo = ( id : string ) => {
setTodos ( prevTodos =>
prevTodos . map ( todo =>
todo . id === id ? { ... todo , completed: ! todo . completed } : todo
)
)
}
const deleteTodo = ( id : string ) => {
setTodos ( prevTodos => prevTodos . filter ( todo => todo . id !== id ))
}
return (
< div >
< h3 > Collaborative Todo List </ h3 >
< 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 } >
< input
type = "checkbox"
checked = { todo . completed }
onChange = { () => toggleTodo ( todo . id ) }
/>
< span style = { {
textDecoration: todo . completed ? 'line-through' : 'none'
} } >
{ todo . text }
</ span >
< button onClick = { () => deleteTodo ( todo . id ) } > Delete </ button >
</ li >
)) }
</ ul >
< p > Total: { todos . length } | Completed: { todos . filter ( t => t . completed ). length } </ p >
</ div >
)
}
Advanced Usage
With Custom Options
function PersistentCounter () {
const [ count , setCount ] = useStateTogether ( 'persistent-counter' , 0 , {
resetOnDisconnect: false , // Keep state when users leave
throttleDelay: 200 // Reduce update frequency
})
return (
< div >
< p > Persistent Count: { count } </ p >
< button onClick = { () => setCount ( c => c + 1 ) } > + </ button >
</ div >
)
}
Multiple Shared States
function GameState () {
const [ score , setScore ] = useStateTogether ( 'game-score' , 0 )
const [ level , setLevel ] = useStateTogether ( 'game-level' , 1 )
const [ players , setPlayers ] = useStateTogether < string []>( 'players' , [])
const levelUp = () => {
if ( score >= level * 100 ) {
setLevel ( l => l + 1 )
setScore ( 0 ) // Reset score for new level
}
}
return (
< div >
< h3 > Game Status </ h3 >
< p > Level: { level } </ p >
< p > Score: { score } / { level * 100 } </ p >
< p > Players: { players . length } </ p >
< button onClick = { () => setScore ( s => s + 10 ) } >
Score Points (+10)
</ button >
< button onClick = { levelUp } disabled = { score < level * 100 } >
Level Up
</ button >
</ div >
)
}
Functional Updates
function StateUpdater () {
const [ data , setData ] = useStateTogether ( 'complex-data' , {
users: [],
lastUpdate: Date . now ()
})
// Always use functional updates for complex state
const addUser = ( username : string ) => {
setData ( prevData => ({
... prevData ,
users: [ ... prevData . users , username ],
lastUpdate: Date . now ()
}))
}
const removeUser = ( username : string ) => {
setData ( prevData => ({
... prevData ,
users: prevData . users . filter ( u => u !== username ),
lastUpdate: Date . now ()
}))
}
return (
< div >
< h3 > User Management </ h3 >
< p > Last Updated: {new Date ( data . lastUpdate ). toLocaleTimeString () } </ p >
< ul >
{ data . users . map ( user => (
< li key = { user } >
{ user }
< button onClick = { () => removeUser ( user ) } > Remove </ button >
</ li >
)) }
</ ul >
< button onClick = { () => addUser ( `User ${ Date . now () } ` ) } >
Add Random User
</ button >
</ div >
)
}
Best Practices
Use Unique Keys Choose descriptive, unique keys for each shared state
// Good
const [ gameScore , setGameScore ] = useStateTogether ( 'game-score' , 0 )
const [ playerName , setPlayerName ] = useStateTogether ( 'player-name' , '' )
// Avoid
const [ data1 , setData1 ] = useStateTogether ( 'data' , 0 )
const [ data2 , setData2 ] = useStateTogether ( 'data' , '' ) // Same key!
Use Functional Updates Always use functional updates for complex state
// Good - functional update
setTodos ( prev => [ ... prev , newTodo ])
// Risky - might overwrite concurrent changes
setTodos ([ ... todos , newTodo ])
Consider Performance Use throttling for frequently updated state
const [ mousePos , setMousePos ] = useStateTogether ( 'mouse' , { x: 0 , y: 0 }, {
throttleDelay: 50 // Update max 20 times per second
})
Initialize Properly Provide sensible initial values
// Good - proper defaults
const [ config , setConfig ] = useStateTogether ( 'config' , {
theme: 'light' ,
language: 'en'
})
// Avoid - undefined state
const [ config , setConfig ] = useStateTogether ( 'config' , undefined )
Common Patterns
Reset Functionality
function ResettableCounter () {
const [ count , setCount ] = useStateTogether ( 'counter' , 0 )
const initialValue = 0
return (
< div >
< p > Count: { count } </ p >
< button onClick = { () => setCount ( c => c + 1 ) } > + </ button >
< button onClick = { () => setCount ( initialValue ) } > Reset </ button >
</ div >
)
}
Conditional Updates
function ConditionalUpdates () {
const [ value , setValue ] = useStateTogether ( 'value' , 0 )
const incrementIfValid = () => {
setValue ( prev => {
// Only update if certain conditions are met
if ( prev < 100 ) {
return prev + 1
}
return prev // No change
})
}
return (
< div >
< p > Value: { value } </ p >
< button onClick = { incrementIfValid } disabled = { value >= 100 } >
Increment (max 100)
</ button >
</ div >
)
}
TypeScript Support
useStateTogether
is fully typed and provides excellent TypeScript support:
// Type is inferred from initial value
const [ count , setCount ] = useStateTogether ( 'counter' , 0 ) // number
// Explicit typing
const [ user , setUser ] = useStateTogether < User | null >( 'current-user' , null )
// Complex types
interface GameState {
level : number
score : number
players : Player []
}
const [ game , setGame ] = useStateTogether < GameState >( 'game' , {
level: 1 ,
score: 0 ,
players: []
})
Troubleshooting
Ensure all components use the same key
parameter
Verify the ReactTogether
provider wraps all components
Check that all users are in the same session
Increase throttleDelay
in options
Use useMemo
or useCallback
for expensive computations
Consider breaking large state objects into smaller pieces
State resets unexpectedly
Check the resetOnDisconnect
option
Ensure initial values are consistent across components
Verify the key string is exactly the same in all uses
useStateTogether
is the foundation of React Together. Once you understand how it works, you can build any kind of collaborative feature by combining it with other hooks and components!
Responses are generated using AI and may contain mistakes.