@socketly/react
v0.1.1
Published
React hooks for Socketly — useChannel, usePresence, and a provider that survives reconnects.
Maintainers
Readme
@socketly/react
React hooks for Socketly — realtime channels and presence, with the reconnect handling already done.
npm install @socketly/client @socketly/reactProvider
Create the client once, outside your components, so a re-render never opens a second connection.
// lib/socketly.ts
import { Socketly } from '@socketly/client';
export const socketly = new Socketly({
key: process.env.NEXT_PUBLIC_SOCKETLY_KEY!,
authEndpoint: '/api/socketly/auth', // needed for private-/presence-
});// app/providers.tsx
'use client';
import { SocketlyProvider } from '@socketly/react';
import { socketly } from '@/lib/socketly';
export function Providers({ children }: { children: React.ReactNode }) {
return <SocketlyProvider client={socketly}>{children}</SocketlyProvider>;
}useChannel
'use client';
import { useState } from 'react';
import { useChannel } from '@socketly/react';
export function Notifications({ userId }: { userId: string }) {
const [items, setItems] = useState<Notification[]>([]);
const { subscribed, error } = useChannel(`private-user-${userId}`, {
notification: ({ data }) => setItems((prev) => [data, ...prev]),
});
if (error) return <p>{error}</p>;
if (!subscribed) return <p>Connecting…</p>;
return <List items={items} />;
}Handlers are read through a ref, so an inline arrow function does not resubscribe on every render. Only the set of event names drives resubscription. This is the failure the previous integration guide shipped: an object literal in a dependency array produced a reconnect loop that looked like a flaky network.
Pass null as the channel name to subscribe to nothing — useful while an id is still loading:
useChannel(roomId ? `presence-room-${roomId}` : null, handlers);usePresence
const { members, count } = usePresence(`presence-room-${roomId}`);
return (
<>
<p>{count} online</p>
{members.map((m) => <Avatar key={m.userId} name={m.userInfo?.name} />)}
</>
);The roster stays in sync as people join and leave. Identities come from inside the payload your server signed, so nobody can appear as someone else.
useConnectionState
const state = useConnectionState();
// 'initialized' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'failed'Backed by useSyncExternalStore, so it is safe with concurrent rendering and server rendering.
Sending
useChannel returns a trigger for client events — browser-to-browser, for things like typing indicators. They must be enabled on the app, only work on private-/presence- channels, and their names must start with client-.
const { trigger } = useChannel(channel, handlers);
trigger('client-typing', { userId });Anything that should be trusted goes through your backend with @socketly/server instead.
Server components
Every hook here is client-side; the entry point is marked 'use client'. Import them from a component that is too.
Full documentation: docs.socketly.co
MIT
