Quickstart
A presence-enabled room with live messages, in five steps. This assumes Next.js with the App Router; the same pieces apply to any React setup.
This is the finished thing, running live — the same five steps below produced it.
public-docs-demoNothing yet. Send from either side.
Nothing yet. Send from either side.
1. Create an app
In the dashboard, create an app and add your development origin (http://localhost:3000) to its allowed origins. You will be shown a public key and a secret key.
2. Install
npm install @socketly/client @socketly/reactAdd the keys to your environment:
NEXT_PUBLIC_SOCKETLY_KEY=pk_app_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyy
SOCKETLY_SECRET=sk_app_xxxxxxxx_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz3. Create the client
// lib/socketly.ts
import { Socketly } from '@socketly/client';
export const socketly = new Socketly({
key: process.env.NEXT_PUBLIC_SOCKETLY_KEY!,
authEndpoint: '/api/socketly/auth',
url: process.env.NEXT_PUBLIC_SOCKETLY_URL, // omit in production
});Then wrap your app:
// 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>;
}4. Add your auth endpoint
Anything beyond a public- channel needs a signature from your backend. The SDK calls this route automatically whenever it subscribes, and again after every reconnect — the signature is bound to the socket id, so it cannot be replayed on a different connection.
// app/api/socketly/auth/route.ts
import { authorizeChannel } from '@socketly/server';
import { getCurrentUser } from '@/lib/session';
export async function POST(request: Request) {
const { socket_id, channel_name } = await request.json();
// You decide who may subscribe. You already know who your user is.
const user = await getCurrentUser();
if (!user) return new Response('Unauthorized', { status: 401 });
if (channel_name.startsWith('private-room-')) {
const roomId = channel_name.replace('private-room-', '');
if (!(await user.canAccessRoom(roomId))) {
return new Response('Forbidden', { status: 403 });
}
}
return Response.json(
authorizeChannel({
secret: process.env.SOCKETLY_SECRET!,
socketId: socket_id,
channel: channel_name,
// Required for presence- channels only.
userData: { user_id: user.id, user_info: { name: user.name } },
}),
);
}5. Use it
'use client';
import { useState } from 'react';
import { useChannel, usePresence } from '@socketly/react';
export function Room({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<string[]>([]);
const channel = `presence-room-${roomId}`;
const { subscribed, error } = useChannel(channel, {
'new-message': (data) => setMessages((m) => [...m, data.data.text]),
});
const { members, count } = usePresence(channel);
if (error) return <p>{error}</p>;
if (!subscribed) return <p>Connecting…</p>;
return (
<div>
<p>{count} online</p>
<ul>
{messages.map((text, i) => (
<li key={i}>{text}</li>
))}
</ul>
</div>
);
}Publishing from your server
Clients can only publish when you explicitly enable client events, and then only to channels they are subscribed to. Most events should come from your backend:
// Anywhere on your server
await fetch(`https://api.socketly.co/v1/apps/${APP_ID}/events`, {
method: 'POST',
headers: {
'content-type': 'application/json',
authorization: `Bearer ${process.env.SOCKETLY_SECRET}`,
},
body: JSON.stringify({
channels: ['presence-room-42'],
event: 'new-message',
data: { text: 'Hello from the server' },
}),
});If something is not working
- Connection refused — check the origin you added to the app matches the one your browser is on, exactly, including the port.
auth_required— the channel isprivate-orpresence-andauthEndpointis not set on the client.auth_invalid— your endpoint signed a different socket id or channel than the one being subscribed. Pass throughsocket_idandchannel_nameunchanged.- Presence roster is empty — presence channels need
userDatawith auser_idin the signature.