Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/realtimekit/patterns.md
1# RealtimeKit Patterns23## UI Kit (Minimal Code)45```tsx6// React7import { RtkMeeting } from '@cloudflare/realtimekit-react-ui';8<RtkMeeting authToken="<token>" onLeave={() => console.log('Left')} />910// Angular11@Component({ template: `<rtk-meeting [authToken]="authToken" (rtkLeave)="onLeave($event)"></rtk-meeting>` })12export class AppComponent { authToken = '<token>'; onLeave(event: unknown) {} }1314// HTML/Web Components15<script type="module" src="https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit-ui/dist/realtimekit-ui/realtimekit-ui.esm.js"></script>16<rtk-meeting id="meeting"></rtk-meeting>17<script>document.getElementById('meeting').authToken = '<token>';</script>18```1920## UI Components2122RealtimeKit provides 133+ pre-built Stencil.js Web Components with framework wrappers:2324### Layout Components25- `<RtkMeeting>` - Full meeting UI (all-in-one)26- `<RtkHeader>`, `<RtkStage>`, `<RtkControlbar>` - Layout sections27- `<RtkSidebar>` - Chat/participants sidebar28- `<RtkGrid>` - Adaptive video grid2930### Control Components31- `<RtkMicToggle>`, `<RtkCameraToggle>` - Media controls32- `<RtkScreenShareToggle>` - Screen sharing33- `<RtkLeaveButton>` - Leave meeting34- `<RtkSettingsModal>` - Device settings3536### Grid Variants37- `<RtkSpotlightGrid>` - Active speaker focus38- `<RtkAudioGrid>` - Audio-only mode39- `<RtkPaginatedGrid>` - Paginated layout4041**See full catalog**: https://docs.realtime.cloudflare.com/ui-kit4243## Core SDK Patterns4445### Basic Setup46```typescript47import RealtimeKitClient from '@cloudflare/realtimekit';4849const meeting = new RealtimeKitClient({ authToken, video: true, audio: true });50meeting.self.on('roomJoined', () => console.log('Joined:', meeting.meta.meetingTitle));51meeting.participants.joined.on('participantJoined', (p) => console.log(`${p.name} joined`));52await meeting.join();53```5455### Video Grid & Device Selection56```typescript57// Video grid58function VideoGrid({ meeting }) {59const [participants, setParticipants] = useState([]);60useEffect(() => {61const update = () => setParticipants(meeting.participants.joined.toArray());62meeting.participants.joined.on('participantJoined', update);63meeting.participants.joined.on('participantLeft', update);64update();65return () => { meeting.participants.joined.off('participantJoined', update); meeting.participants.joined.off('participantLeft', update); };66}, [meeting]);67return <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))' }}>68{participants.map(p => <VideoTile key={p.id} participant={p} />)}69</div>;70}7172function VideoTile({ participant }) {73const videoRef = useRef<HTMLVideoElement>(null);74useEffect(() => {75if (videoRef.current && participant.videoTrack) videoRef.current.srcObject = new MediaStream([participant.videoTrack]);76}, [participant.videoTrack]);77return <div><video ref={videoRef} autoPlay playsInline muted /><div>{participant.name}</div></div>;78}7980// Device selection81const devices = await meeting.self.getAllDevices();82const switchCamera = (deviceId: string) => {83const device = devices.find(d => d.deviceId === deviceId);84if (device) await meeting.self.setDevice(device);85};86```8788## React Hooks (Official)8990```typescript91import { useRealtimeKitClient, useRealtimeKitSelector } from '@cloudflare/realtimekit-react-ui';9293function MyComponent() {94const [meeting, initMeeting] = useRealtimeKitClient();95const audioEnabled = useRealtimeKitSelector(m => m.self.audioEnabled);96const participantCount = useRealtimeKitSelector(m => m.participants.joined.size());9798useEffect(() => { initMeeting({ authToken: '<token>' }); }, []);99100return <div>101<button onClick={() => meeting?.self.enableAudio()}>{audioEnabled ? 'Mute' : 'Unmute'}</button>102<span>{participantCount} participants</span>103</div>;104}105```106107**Benefits:** Automatic re-renders, memoized selectors, type-safe108109## Waitlist Handling110111```typescript112// Monitor waitlist113meeting.participants.waitlisted.on('participantJoined', (participant) => {114console.log(`${participant.name} is waiting`);115// Show admin UI to approve/reject116});117118// Approve from waitlist (backend only)119await fetch(120`https://api.cloudflare.com/client/v4/accounts/${accountId}/realtime/kit/${appId}/meetings/${meetingId}/active-session/waitlist/approve`,121{122method: 'POST',123headers: { 'Authorization': `Bearer ${apiToken}` },124body: JSON.stringify({ user_ids: [participant.userId] })125}126);127128// Client receives automatic transition when approved129meeting.self.on('roomJoined', () => console.log('Approved and joined'));130```131132## Audio-Only Mode133134```typescript135const meeting = new RealtimeKitClient({136authToken: '<token>',137video: false, // Disable video138audio: true,139mediaConfiguration: {140audio: {141echoCancellation: true,142noiseSuppression: true,143autoGainControl: true144}145}146});147148// Use audio grid component149import { RtkAudioGrid } from '@cloudflare/realtimekit-react-ui';150<RtkAudioGrid meeting={meeting} />151```152153## Addon System154155```typescript156// List available addons157meeting.plugins.all.forEach(plugin => {158console.log(plugin.id, plugin.name, plugin.active);159});160161// Activate collaborative app162await meeting.plugins.activate('whiteboard-addon-id');163164// Listen for activations165meeting.plugins.on('pluginActivated', ({ plugin }) => {166console.log(`${plugin.name} activated`);167});168169// Deactivate170await meeting.plugins.deactivate();171```172173## Backend Integration174175### Token Generation (Workers)176```typescript177export interface Env { CLOUDFLARE_API_TOKEN: string; CLOUDFLARE_ACCOUNT_ID: string; REALTIMEKIT_APP_ID: string; }178179export default {180async fetch(request: Request, env: Env): Promise<Response> {181const url = new URL(request.url);182183if (url.pathname === '/api/join-meeting') {184const { meetingId, userName, presetName } = await request.json();185const response = await fetch(186`https://api.cloudflare.com/client/v4/accounts/${env.CLOUDFLARE_ACCOUNT_ID}/realtime/kit/${env.REALTIMEKIT_APP_ID}/meetings/${meetingId}/participants`,187{188method: 'POST',189headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.CLOUDFLARE_API_TOKEN}` },190body: JSON.stringify({ name: userName, preset_name: presetName })191}192);193const data = await response.json();194return Response.json({ authToken: data.result.authToken });195}196197return new Response('Not found', { status: 404 });198}199};200```201202## Best Practices203204### Security2051. **Never expose API tokens client-side** - Generate participant tokens server-side only2062. **Don't reuse participant tokens** - Generate fresh token per session, use refresh endpoint if expired2073. **Use custom participant IDs** - Map to your user system for cross-session tracking208209### Performance2101. **Event-driven updates** - Listen to events, don't poll. Use `toArray()` only when needed2112. **Media quality constraints** - Set appropriate resolution/bitrate limits based on network conditions2123. **Device management** - Enable `autoSwitchAudioDevice` for better UX, handle device list updates213214### Architecture2151. **Separate Apps for environments** - staging vs production to prevent data mixing2162. **Preset strategy** - Create presets at App level, reuse across meetings2173. **Token management** - Backend generates tokens, frontend receives via authenticated endpoint218219## In This Reference220- [README.md](README.md) - Overview, core concepts, quick start221- [configuration.md](configuration.md) - SDK config, presets, wrangler setup222- [api.md](api.md) - Client SDK APIs, REST endpoints223- [gotchas.md](gotchas.md) - Common issues, troubleshooting, limits224