All skills
Skillintermediate
RealtimeKit Patterns
```tsx // React import { RtkMeeting } from '@cloudflare/realtimekit-react-ui'; " onLeave={() => console.log('Left')} />
Claude Code Knowledge Pack7/10/2026
Overview
RealtimeKit Patterns
UI Kit (Minimal Code)
// React
" onLeave={() => console.log('Left')} />
// Angular
@Component({ template: `<rtk-meeting [authToken]="authToken" (rtkLeave)="onLeave($event)"></rtk-meeting>` })
// HTML/Web Components
<script type="module" src="https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui/dist/realtimekit-ui/realtimekit-ui.esm.js"></script>
<rtk-meeting id="meeting"></rtk-meeting>
<script>document.getElementById('meeting').authToken = '<token>';</script>
UI Components
RealtimeKit provides 133+ pre-built Stencil.js Web Components with framework wrappers:
Layout Components
- `` - Full meeting UI (all-in-one)
,, `` - Layout sections- `` - Chat/participants sidebar
- `` - Adaptive video grid
Control Components
,- Media controls- `` - Screen sharing
- `` - Leave meeting
- `` - Device settings
Grid Variants
- `` - Active speaker focus
- `` - Audio-only mode
- `` - Paginated layout
See full catalog: https://docs.realtime.cloudflare.com/ui-kit
Core SDK Patterns
Basic Setup
const meeting = new RealtimeKitClient({ authToken, video: true, audio: true });
meeting.self.on('roomJoined', () => console.log('Joined:', meeting.meta.meetingTitle));
meeting.participants.joined.on('participantJoined', (p) => console.log(`${p.name} joined`));
await meeting.join();
Video Grid & Device Selection
// Video grid
function VideoGrid({ meeting }) {
const [participants, setParticipants] = useState([]);
useEffect(() => {
const update = () => setParticipants(meeting.participants.joined.toArray());
meeting.participants.joined.on('participantJoined', update);
meeting.participants.joined.on('participantLeft', update);
update();
return () => { meeting.participants.joined.off('participantJoined', update); meeting.participants.joined.off('participantLeft', update); };
}, [meeting]);
return <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))' }}>
{participants.map(p => )}
</div>;
}
function VideoTile({ participant }) {
const videoRef = useRef(null);
useEffect(() => {
if (videoRef.current && participant.videoTrack) videoRef.current.srcObject = new MediaStream([participant.videoTrack]);
}, [participant.videoTrack]);
return <div><video ref={videoRef} autoPlay playsInline muted /><div>{participant.name}</div></div>;
}
// Device selection
const devices = await meeting.self.getAllDevices();
const switchCamera = (deviceId: string) => {
const device = devices.find(d => d.deviceId === deviceId);
if (device) await meeting.self.setDevice(device);
};
React Hooks (Official)
function MyComponent() {
const [meeting, initMeeting] = useRealtimeKitClient();
const audioEnabled = useRealtimeKitSelector(m => m.self.audioEnabled);
const participantCount = useRealtimeKitSelector(m => m.participants.joined.size());
useEffect(() => { initMeeting({ authToken: '<token>' }); }, []);
return <div>
<button onClick={() => meeting?.self.enableAudio()}>{audioEnabled ? 'Mute' : 'Unmute'}</button>
<span>{participantCount} participants</span>
</div>;
}
Benefits: Automatic re-renders, memoized selectors, type-safe
Waitlist Handling
// Monitor waitlist
meeting.participants.waitlisted.on('participantJoined', (participant) => {
console.log(`${participant.name} is waiting`);
// Show admin UI to approve/reject
});
// Approve from waitlist (backend only)
await fetch(
`https://api.cloudflare.com/client/v4/accounts/${accountId}/realtime/kit/${appId}/meetings/${meetingId}/active-session/waitlist/approve`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${apiToken}` },
body: JSON.stringify({ user_ids: [participant.userId] })
}
);
// Client receives automatic transition when approved
meeting.self.on('roomJoined', () => console.log('Approved and joined'));
Audio-Only Mode
const meeting = new RealtimeKitClient({
authToken: '<token>',
video: false, // Disable video
audio: true,
mediaConfiguration: {
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
}
}
});
// Use audio grid component
Addon System
// List available addons
meeting.plugins.all.forEach(plugin => {
console.log(plugin.id, plugin.name, plugin.active);
});
// Activate collaborative app
await meeting.plugins.activate('whiteboard-addon-id');
// Listen for activations
meeting.plugins.on('pluginActivated', ({ plugin }) => {
console.log(`${plugin.name} activated`);
});
// Deactivate
await meeting.plugins.deactivate();
Backend Integration
Token Generation (Workers)
async fetch(request: Request, env: Env): Promise {
const url = new URL(request.url);
if (url.pathname === '/api/join-meeting') {
const { meetingId, userName, presetName } = await request.json();
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/realtime/kit/${env.REALTIMEKIT_APP_ID}/meetings/${meetingId}/participants`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.CLOUDFLARE_API_TOKEN}` },
body: JSON.stringify({ name: userName, preset_name: presetName })
}
);
const data = await response.json();
return Response.json({ authToken: data.result.authToken });
}
return new Response('Not found', { status: 404 });
}
};
Best Practices
Security
- Never expose API tokens client-side - Generate participant tokens server-side only
- Don't reuse participant tokens - Generate fresh token per session, use refresh endpoint if expired
- Use custom participant IDs - Map to your user system for cross-session tracking
Performance
- Event-driven updates - Listen to events, don't poll. Use
toArray()only when needed - Media quality constraints - Set appropriate resolution/bitrate limits based on network conditions
- Device management - Enable
autoSwitchAudioDevicefor better UX, handle device list updates
Architecture
- Separate Apps for environments - staging vs production to prevent data mixing
- Preset strategy - Create presets at App level, reuse across meetings
- Token management - Backend generates tokens, frontend receives via authenticated endpoint
In This Reference
- README.md - Overview, core concepts, quick start
- configuration.md - SDK config, presets, wrangler setup
- api.md - Client SDK APIs, REST endpoints
- gotchas.md - Common issues, troubleshooting, limits