@better-state/react
v0.2.0
Published
React hooks for Better-State — real-time synced state with one line of code
Maintainers
Readme
@better-state/react
React hooks for Better-State. Real-time synced state with one line of code.
Installation
npm install @better-state/client @better-state/react
# or
pnpm add @better-state/client @better-state/reactRequires React 18+ and a running @better-state/server.
Quick Start
import { BetterStateProvider, useBetterState } from '@better-state/react'
function App() {
return (
<BetterStateProvider
url="http://localhost:3001"
options={{ apiKey: 'your-api-key' }}
>
<Counter />
</BetterStateProvider>
)
}
function Counter() {
const [count, setCount, updateCount] = useBetterState('counter', 0)
return (
<div>
<h1>{count}</h1>
<button onClick={() => updateCount(n => n + 1)}>+1</button>
<button onClick={() => updateCount(n => n - 1)}>-1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
)
}Open the same page in two tabs. The counter syncs in real-time.
API
<BetterStateProvider>
Wraps your app and creates a single Better-State client for the component tree.
<BetterStateProvider
url="http://localhost:3001"
options={{
apiKey: string, // required
namespace?: string, // default: 'default'
debug?: boolean, // default: false
}}
>
{children}
</BetterStateProvider>Place it once at the root of your app (or per-feature if you need multiple servers).
useBetterState(key, initialValue)
Synced state hook. Works like useState, but the value is shared across all connected clients.
const [value, set, update] = useBetterState('my-key', initialValue)| Return | Type | Description |
|--------|------|-------------|
| value | T | Current value (reactive) |
| set | (value: T) => void | Replace the value |
| update | (fn: (prev: T) => T) => void | Transform the value |
Updates are optimistic — the UI updates instantly, then syncs to the server in the background.
function TodoList() {
const [todos, setTodos, updateTodos] = useBetterState('todos', [])
const addTodo = (text: string) => {
updateTodos(list => [...list, { id: crypto.randomUUID(), text, done: false }])
}
const toggleTodo = (id: string) => {
updateTodos(list =>
list.map(t => t.id === id ? { ...t, done: !t.done } : t)
)
}
return (
<ul>
{todos.map(t => (
<li key={t.id} onClick={() => toggleTodo(t.id)}>
{t.done ? '✓' : '○'} {t.text}
</li>
))}
</ul>
)
}useConnectionStatus()
Returns the current connection status. Re-renders when it changes.
import { useConnectionStatus } from '@better-state/react'
function StatusIndicator() {
const status = useConnectionStatus()
return (
<span>
{status === 'connected' && 'Online'}
{status === 'connecting' && 'Connecting...'}
{status === 'disconnected' && 'Offline'}
</span>
)
}Returns: 'connecting' | 'connected' | 'disconnected'
useBetterStateClient()
Returns the underlying BetterStateClient instance for advanced use cases (disconnect, error handling, conflict listeners).
import { useBetterStateClient } from '@better-state/react'
function Settings() {
const client = useBetterStateClient()
useEffect(() => {
const unsub = client.onError(err => {
console.error(err.code, err.message)
})
return unsub
}, [client])
return <button onClick={() => client.disconnect()}>Disconnect</button>
}Full Example
import { BetterStateProvider, useBetterState, useConnectionStatus } from '@better-state/react'
function App() {
return (
<BetterStateProvider
url="http://localhost:3001"
options={{ apiKey: 'your-api-key' }}
>
<StatusBar />
<Counter />
</BetterStateProvider>
)
}
function StatusBar() {
const status = useConnectionStatus()
return <div>{status === 'connected' ? 'Online' : 'Offline'}</div>
}
function Counter() {
const [count, , updateCount] = useBetterState('counter', 0)
return (
<div>
<h1>{count}</h1>
<button onClick={() => updateCount(n => n + 1)}>+1</button>
</div>
)
}
export default AppPeer Dependencies
react>= 18.0.0@better-state/client>= 0.1.0
License
MIT
