@usefy/screen-recorder 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +631 -0
- package/dist/index.d.mts +1092 -0
- package/dist/index.d.ts +1092 -0
- package/dist/index.js +3473 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3410 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +1267 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/mirunamu00/usefy/master/assets/logo.png" alt="usefy logo" width="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@usefy/screen-recorder</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>React component for screen recording with preview and download</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@usefy/screen-recorder">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/@usefy/screen-recorder.svg?style=flat-square&color=007acc" alt="npm version" />
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@usefy/screen-recorder">
|
|
16
|
+
<img src="https://img.shields.io/npm/dm/@usefy/screen-recorder.svg?style=flat-square&color=007acc" alt="npm downloads" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://bundlephobia.com/package/@usefy/screen-recorder">
|
|
19
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@usefy/screen-recorder?style=flat-square&color=007acc" alt="bundle size" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/mirunamu00/usefy/blob/master/LICENSE">
|
|
22
|
+
<img src="https://img.shields.io/npm/l/@usefy/screen-recorder.svg?style=flat-square&color=007acc" alt="license" />
|
|
23
|
+
</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<a href="#installation">Installation</a> •
|
|
28
|
+
<a href="#quick-start">Quick Start</a> •
|
|
29
|
+
<a href="#features">Features</a> •
|
|
30
|
+
<a href="#api-reference">API Reference</a> •
|
|
31
|
+
<a href="#examples">Examples</a> •
|
|
32
|
+
<a href="#browser-support">Browser Support</a>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<a href="https://mirunamu00.github.io/usefy/?path=/story/kits-screenrecorder--overview" target="_blank" rel="noopener noreferrer">
|
|
37
|
+
<strong>View Storybook Demo</strong>
|
|
38
|
+
</a>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
`@usefy/screen-recorder` is a React component for capturing screen recordings directly in the browser. It provides a complete UI solution with floating trigger, recording controls, countdown, preview modal, and download functionality.
|
|
46
|
+
|
|
47
|
+
**Part of the [@usefy](https://www.npmjs.com/org/usefy) ecosystem.**
|
|
48
|
+
|
|
49
|
+
### Why screen-recorder?
|
|
50
|
+
|
|
51
|
+
- **Zero Backend Required** — Pure browser-based recording using native MediaRecorder API
|
|
52
|
+
- **Complete UI Solution** — Ready-to-use floating controls with preview modal
|
|
53
|
+
- **Flexible Output** — WebM video with configurable quality presets
|
|
54
|
+
- **Developer Friendly** — Both component and headless hook APIs
|
|
55
|
+
- **Privacy First** — All processing happens client-side, no data leaves the browser
|
|
56
|
+
- **TypeScript First** — Full type safety with comprehensive exported interfaces
|
|
57
|
+
- **SSR Compatible** — Safe to use with Next.js, Remix, and other SSR frameworks
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# npm
|
|
65
|
+
npm install @usefy/screen-recorder
|
|
66
|
+
|
|
67
|
+
# yarn
|
|
68
|
+
yarn add @usefy/screen-recorder
|
|
69
|
+
|
|
70
|
+
# pnpm
|
|
71
|
+
pnpm add @usefy/screen-recorder
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Peer Dependencies
|
|
75
|
+
|
|
76
|
+
This package requires React 18+:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
82
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { ScreenRecorder } from "@usefy/screen-recorder";
|
|
93
|
+
|
|
94
|
+
function App() {
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
<YourApp />
|
|
98
|
+
{/* Add floating recorder button */}
|
|
99
|
+
<ScreenRecorder
|
|
100
|
+
onRecordingStop={(result) => {
|
|
101
|
+
console.log("Recording complete:", result);
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> **Note:** Styles are automatically injected. No CSS import required!
|
|
110
|
+
|
|
111
|
+
A floating trigger button appears in the corner. Click to start recording, and the browser will prompt you to select a screen, window, or tab to record.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Features
|
|
116
|
+
|
|
117
|
+
### Screen Capture
|
|
118
|
+
|
|
119
|
+
Record entire screen, specific windows, or browser tabs using the native Screen Capture API:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<ScreenRecorder
|
|
123
|
+
audio={true} // Include system audio
|
|
124
|
+
quality="high"
|
|
125
|
+
maxDuration={300} // 5 minutes max
|
|
126
|
+
/>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Capture Options:**
|
|
130
|
+
- **Entire Screen** — Record everything on your display
|
|
131
|
+
- **Application Window** — Record a specific application
|
|
132
|
+
- **Browser Tab** — Record a single browser tab (with audio)
|
|
133
|
+
|
|
134
|
+
### Recording Controls
|
|
135
|
+
|
|
136
|
+
Full control over the recording process:
|
|
137
|
+
|
|
138
|
+
| Feature | Description |
|
|
139
|
+
|---------|-------------|
|
|
140
|
+
| **Start/Stop** | Basic recording controls |
|
|
141
|
+
| **Pause/Resume** | Pause recording without stopping |
|
|
142
|
+
| **Timer Display** | Shows elapsed and remaining time |
|
|
143
|
+
| **Max Duration** | Auto-stop at configured limit (or unlimited with `Infinity`) |
|
|
144
|
+
| **Countdown** | 3-2-1 countdown before recording starts |
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<ScreenRecorder
|
|
148
|
+
countdown={3} // 3-2-1 countdown
|
|
149
|
+
maxDuration={60} // 1 minute max
|
|
150
|
+
showTimer={true} // Display timer
|
|
151
|
+
onPause={() => console.log("Paused")}
|
|
152
|
+
onResume={() => console.log("Resumed")}
|
|
153
|
+
/>
|
|
154
|
+
|
|
155
|
+
// Unlimited recording (no auto-stop)
|
|
156
|
+
<ScreenRecorder maxDuration={Infinity} />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Preview & Download
|
|
160
|
+
|
|
161
|
+
Review your recording before saving:
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
<ScreenRecorder
|
|
165
|
+
showPreview={true} // Show preview modal after recording
|
|
166
|
+
autoDownload={false} // Manual download
|
|
167
|
+
filename="my-recording" // Custom filename
|
|
168
|
+
onDownload={(result) => {
|
|
169
|
+
console.log("Downloaded:", result.size, "bytes");
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Preview Modal Features:**
|
|
175
|
+
- Video player with playback controls
|
|
176
|
+
- Recording info (duration, size, format)
|
|
177
|
+
- Download button
|
|
178
|
+
- Re-record option
|
|
179
|
+
- Done/Close button
|
|
180
|
+
|
|
181
|
+
### Quality Presets
|
|
182
|
+
|
|
183
|
+
Configure video quality with built-in presets:
|
|
184
|
+
|
|
185
|
+
| Preset | Bitrate | Frame Rate | Use Case |
|
|
186
|
+
|--------|---------|------------|----------|
|
|
187
|
+
| `low` | 1 Mbps | 15 fps | Smaller files, basic quality |
|
|
188
|
+
| `medium` | 2.5 Mbps | 30 fps | Balanced quality/size (default) |
|
|
189
|
+
| `high` | 5 Mbps | 60 fps | Best quality, larger files |
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<ScreenRecorder quality="high" />
|
|
193
|
+
|
|
194
|
+
// Or custom configuration
|
|
195
|
+
<ScreenRecorder
|
|
196
|
+
quality={{
|
|
197
|
+
videoBitsPerSecond: 4_000_000,
|
|
198
|
+
frameRate: 30,
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Audio Recording
|
|
204
|
+
|
|
205
|
+
Capture system audio along with video:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
<ScreenRecorder
|
|
209
|
+
audio={true} // Include system/tab audio
|
|
210
|
+
/>
|
|
211
|
+
|
|
212
|
+
// Or detailed configuration
|
|
213
|
+
<ScreenRecorder
|
|
214
|
+
audio={{
|
|
215
|
+
system: true, // System audio
|
|
216
|
+
microphone: true, // Microphone (future)
|
|
217
|
+
}}
|
|
218
|
+
/>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
> **Note:** System audio capture works best when recording browser tabs. Some browsers may require user permission.
|
|
222
|
+
|
|
223
|
+
### Position Options
|
|
224
|
+
|
|
225
|
+
Place the trigger button anywhere:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
<ScreenRecorder position="bottom-right" /> // Default
|
|
229
|
+
<ScreenRecorder position="bottom-left" />
|
|
230
|
+
<ScreenRecorder position="top-right" />
|
|
231
|
+
<ScreenRecorder position="top-left" />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Custom Trigger
|
|
235
|
+
|
|
236
|
+
Customize the trigger button appearance:
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
<ScreenRecorder
|
|
240
|
+
triggerContent={
|
|
241
|
+
<span className="flex items-center gap-2">
|
|
242
|
+
<CameraIcon />
|
|
243
|
+
Record Screen
|
|
244
|
+
</span>
|
|
245
|
+
}
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Theme Support
|
|
250
|
+
|
|
251
|
+
The component supports light and dark themes:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
<ScreenRecorder theme="dark" /> // Force dark
|
|
255
|
+
<ScreenRecorder theme="light" /> // Force light
|
|
256
|
+
<ScreenRecorder theme="system" /> // Follow OS (default)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## API Reference
|
|
262
|
+
|
|
263
|
+
### ScreenRecorder Props
|
|
264
|
+
|
|
265
|
+
#### Recording Options
|
|
266
|
+
|
|
267
|
+
| Prop | Type | Default | Description |
|
|
268
|
+
|------|------|---------|-------------|
|
|
269
|
+
| `maxDuration` | `number` | `300` | Maximum recording duration in seconds (use `Infinity` for unlimited) |
|
|
270
|
+
| `countdown` | `number \| false` | `3` | Countdown before recording (false to disable) |
|
|
271
|
+
| `audio` | `boolean \| AudioConfig` | `false` | Include audio in recording |
|
|
272
|
+
| `quality` | `'low' \| 'medium' \| 'high' \| QualityPreset` | `'medium'` | Video quality preset |
|
|
273
|
+
| `format` | `'webm' \| 'mp4'` | `'webm'` | Output format |
|
|
274
|
+
| `mimeType` | `string` | `'video/webm;codecs=vp9'` | MIME type for MediaRecorder |
|
|
275
|
+
|
|
276
|
+
#### UI Options
|
|
277
|
+
|
|
278
|
+
| Prop | Type | Default | Description |
|
|
279
|
+
|------|------|---------|-------------|
|
|
280
|
+
| `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | `'bottom-right'` | Trigger button position |
|
|
281
|
+
| `triggerContent` | `ReactNode` | - | Custom trigger button content |
|
|
282
|
+
| `showPreview` | `boolean` | `true` | Show preview modal after recording |
|
|
283
|
+
| `showTimer` | `boolean` | `true` | Show timer during recording |
|
|
284
|
+
| `zIndex` | `number` | `9999` | Z-index for floating elements |
|
|
285
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Theme setting |
|
|
286
|
+
| `className` | `string` | - | Additional CSS class |
|
|
287
|
+
| `filename` | `string \| ((timestamp: Date) => string)` | `'screen-recording'` | Download filename |
|
|
288
|
+
|
|
289
|
+
#### Callbacks
|
|
290
|
+
|
|
291
|
+
| Prop | Type | Description |
|
|
292
|
+
|------|------|-------------|
|
|
293
|
+
| `onRecordingStart` | `() => void` | Called when recording starts |
|
|
294
|
+
| `onRecordingStop` | `(result: RecordingResult) => void` | Called when recording stops |
|
|
295
|
+
| `onPause` | `() => void` | Called when recording is paused |
|
|
296
|
+
| `onResume` | `() => void` | Called when recording is resumed |
|
|
297
|
+
| `onDownload` | `(result: RecordingResult) => void` | Called when user downloads |
|
|
298
|
+
| `onError` | `(error: ScreenRecorderError) => void` | Called on error |
|
|
299
|
+
| `onPermissionDenied` | `() => void` | Called when user denies permission |
|
|
300
|
+
| `onTick` | `(elapsed: number, remaining: number \| null) => void` | Called every second |
|
|
301
|
+
|
|
302
|
+
#### Advanced
|
|
303
|
+
|
|
304
|
+
| Prop | Type | Default | Description |
|
|
305
|
+
|------|------|---------|-------------|
|
|
306
|
+
| `autoDownload` | `boolean` | `false` | Auto-download after recording |
|
|
307
|
+
| `disabled` | `boolean` | `false` | Disable the component |
|
|
308
|
+
| `renderMode` | `'portal' \| 'inline'` | `'portal'` | Render mode for floating UI |
|
|
309
|
+
|
|
310
|
+
### useScreenRecorder Hook
|
|
311
|
+
|
|
312
|
+
For headless usage without the built-in UI:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
import { useScreenRecorder } from "@usefy/screen-recorder";
|
|
316
|
+
|
|
317
|
+
function CustomRecorder() {
|
|
318
|
+
const {
|
|
319
|
+
// State
|
|
320
|
+
state, // 'idle' | 'requesting' | 'countdown' | 'recording' | 'paused' | 'stopped' | 'error'
|
|
321
|
+
isRecording, // boolean
|
|
322
|
+
isPaused, // boolean
|
|
323
|
+
isCountingDown, // boolean
|
|
324
|
+
countdownValue, // number | null
|
|
325
|
+
elapsed, // number (seconds)
|
|
326
|
+
remaining, // number | null (seconds)
|
|
327
|
+
elapsedFormatted,// string ('MM:SS')
|
|
328
|
+
result, // RecordingResult | null
|
|
329
|
+
error, // ScreenRecorderError | null
|
|
330
|
+
isSupported, // boolean
|
|
331
|
+
|
|
332
|
+
// Actions
|
|
333
|
+
start, // () => Promise<void>
|
|
334
|
+
stop, // () => void
|
|
335
|
+
pause, // () => void
|
|
336
|
+
resume, // () => void
|
|
337
|
+
togglePause, // () => void
|
|
338
|
+
download, // (filename?: string) => void
|
|
339
|
+
reset, // () => void
|
|
340
|
+
getPreviewUrl, // () => string | null
|
|
341
|
+
revokePreviewUrl,// () => void
|
|
342
|
+
} = useScreenRecorder({
|
|
343
|
+
maxDuration: 120,
|
|
344
|
+
audio: true,
|
|
345
|
+
countdown: 3,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Build your own UI...
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Types
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// Recording state
|
|
356
|
+
type RecordingState =
|
|
357
|
+
| 'idle'
|
|
358
|
+
| 'requesting'
|
|
359
|
+
| 'countdown'
|
|
360
|
+
| 'recording'
|
|
361
|
+
| 'paused'
|
|
362
|
+
| 'stopped'
|
|
363
|
+
| 'error';
|
|
364
|
+
|
|
365
|
+
// Recording result
|
|
366
|
+
interface RecordingResult {
|
|
367
|
+
blob: Blob;
|
|
368
|
+
url: string;
|
|
369
|
+
duration: number;
|
|
370
|
+
size: number;
|
|
371
|
+
mimeType: string;
|
|
372
|
+
timestamp: Date;
|
|
373
|
+
hasAudio: boolean;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Error
|
|
377
|
+
interface ScreenRecorderError {
|
|
378
|
+
code: ScreenRecorderErrorCode;
|
|
379
|
+
message: string;
|
|
380
|
+
originalError?: Error;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
type ScreenRecorderErrorCode =
|
|
384
|
+
| 'PERMISSION_DENIED'
|
|
385
|
+
| 'NOT_SUPPORTED'
|
|
386
|
+
| 'MEDIA_RECORDER_ERROR'
|
|
387
|
+
| 'STREAM_ENDED'
|
|
388
|
+
| 'NO_STREAM'
|
|
389
|
+
| 'ENCODING_ERROR'
|
|
390
|
+
| 'UNKNOWN';
|
|
391
|
+
|
|
392
|
+
// Quality preset
|
|
393
|
+
interface QualityPreset {
|
|
394
|
+
videoBitsPerSecond: number;
|
|
395
|
+
width?: number;
|
|
396
|
+
height?: number;
|
|
397
|
+
frameRate?: number;
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Examples
|
|
404
|
+
|
|
405
|
+
### Basic Usage
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { ScreenRecorder } from "@usefy/screen-recorder";
|
|
409
|
+
|
|
410
|
+
function App() {
|
|
411
|
+
return (
|
|
412
|
+
<div>
|
|
413
|
+
<h1>My Application</h1>
|
|
414
|
+
<ScreenRecorder />
|
|
415
|
+
</div>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Bug Report Integration
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { ScreenRecorder, RecordingResult } from "@usefy/screen-recorder";
|
|
424
|
+
import { useState } from "react";
|
|
425
|
+
|
|
426
|
+
function BugReportForm() {
|
|
427
|
+
const [recording, setRecording] = useState<RecordingResult | null>(null);
|
|
428
|
+
|
|
429
|
+
const handleSubmit = async () => {
|
|
430
|
+
const formData = new FormData();
|
|
431
|
+
formData.append('description', description);
|
|
432
|
+
if (recording) {
|
|
433
|
+
formData.append('video', recording.blob, 'bug-recording.webm');
|
|
434
|
+
}
|
|
435
|
+
await submitBugReport(formData);
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
return (
|
|
439
|
+
<form onSubmit={handleSubmit}>
|
|
440
|
+
<textarea placeholder="Describe the bug..." />
|
|
441
|
+
|
|
442
|
+
<ScreenRecorder
|
|
443
|
+
position="bottom-left"
|
|
444
|
+
maxDuration={60}
|
|
445
|
+
onRecordingStop={setRecording}
|
|
446
|
+
triggerContent={
|
|
447
|
+
recording ? "✓ Recording attached" : "📹 Record screen"
|
|
448
|
+
}
|
|
449
|
+
/>
|
|
450
|
+
|
|
451
|
+
{recording && (
|
|
452
|
+
<div>
|
|
453
|
+
<video src={recording.url} controls width={300} />
|
|
454
|
+
<p>Duration: {recording.duration}s, Size: {(recording.size / 1024 / 1024).toFixed(2)} MB</p>
|
|
455
|
+
<button type="button" onClick={() => setRecording(null)}>Remove</button>
|
|
456
|
+
</div>
|
|
457
|
+
)}
|
|
458
|
+
|
|
459
|
+
<button type="submit">Submit Bug Report</button>
|
|
460
|
+
</form>
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Tutorial Recording
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
<ScreenRecorder
|
|
469
|
+
audio={true} // Include audio for narration
|
|
470
|
+
quality="high" // Best quality for tutorials
|
|
471
|
+
maxDuration={600} // 10 minutes
|
|
472
|
+
countdown={5} // 5 second countdown
|
|
473
|
+
filename={(timestamp) => `tutorial-${timestamp.toISOString().split('T')[0]}`}
|
|
474
|
+
onRecordingStop={(result) => {
|
|
475
|
+
// Upload to video hosting
|
|
476
|
+
uploadToYouTube(result.blob);
|
|
477
|
+
}}
|
|
478
|
+
/>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Custom Hook Usage
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { useScreenRecorder } from "@usefy/screen-recorder";
|
|
485
|
+
|
|
486
|
+
function CustomRecorder() {
|
|
487
|
+
const {
|
|
488
|
+
state,
|
|
489
|
+
isRecording,
|
|
490
|
+
isPaused,
|
|
491
|
+
elapsed,
|
|
492
|
+
elapsedFormatted,
|
|
493
|
+
result,
|
|
494
|
+
start,
|
|
495
|
+
stop,
|
|
496
|
+
pause,
|
|
497
|
+
resume,
|
|
498
|
+
download,
|
|
499
|
+
reset,
|
|
500
|
+
isSupported,
|
|
501
|
+
error,
|
|
502
|
+
} = useScreenRecorder({
|
|
503
|
+
maxDuration: 120,
|
|
504
|
+
audio: true,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (!isSupported) {
|
|
508
|
+
return <p>Screen recording is not supported in your browser.</p>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return (
|
|
512
|
+
<div className="custom-recorder">
|
|
513
|
+
<p>State: {state}</p>
|
|
514
|
+
<p>Time: {elapsedFormatted}</p>
|
|
515
|
+
|
|
516
|
+
{state === 'idle' && (
|
|
517
|
+
<button onClick={start}>Start Recording</button>
|
|
518
|
+
)}
|
|
519
|
+
|
|
520
|
+
{isRecording && (
|
|
521
|
+
<>
|
|
522
|
+
<button onClick={pause}>Pause</button>
|
|
523
|
+
<button onClick={stop}>Stop</button>
|
|
524
|
+
</>
|
|
525
|
+
)}
|
|
526
|
+
|
|
527
|
+
{isPaused && (
|
|
528
|
+
<>
|
|
529
|
+
<button onClick={resume}>Resume</button>
|
|
530
|
+
<button onClick={stop}>Stop</button>
|
|
531
|
+
</>
|
|
532
|
+
)}
|
|
533
|
+
|
|
534
|
+
{result && (
|
|
535
|
+
<div>
|
|
536
|
+
<video src={result.url} controls />
|
|
537
|
+
<button onClick={() => download('my-recording')}>Download</button>
|
|
538
|
+
<button onClick={reset}>New Recording</button>
|
|
539
|
+
</div>
|
|
540
|
+
)}
|
|
541
|
+
|
|
542
|
+
{error && <p className="error">Error: {error.message}</p>}
|
|
543
|
+
</div>
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### With Callbacks
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
<ScreenRecorder
|
|
552
|
+
onRecordingStart={() => {
|
|
553
|
+
console.log('Recording started');
|
|
554
|
+
analytics.track('recording_started');
|
|
555
|
+
}}
|
|
556
|
+
onRecordingStop={(result) => {
|
|
557
|
+
console.log('Recording stopped:', result.duration, 'seconds');
|
|
558
|
+
analytics.track('recording_completed', {
|
|
559
|
+
duration: result.duration,
|
|
560
|
+
size: result.size,
|
|
561
|
+
hasAudio: result.hasAudio,
|
|
562
|
+
});
|
|
563
|
+
}}
|
|
564
|
+
onError={(error) => {
|
|
565
|
+
console.error('Recording error:', error.code, error.message);
|
|
566
|
+
errorReporting.capture(error.originalError);
|
|
567
|
+
}}
|
|
568
|
+
onPermissionDenied={() => {
|
|
569
|
+
toast.error('Please allow screen sharing to record');
|
|
570
|
+
}}
|
|
571
|
+
/>
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Browser Support
|
|
577
|
+
|
|
578
|
+
Screen recording uses the `getDisplayMedia` API which is a desktop-only feature:
|
|
579
|
+
|
|
580
|
+
| Browser | Version | Support Level | Notes |
|
|
581
|
+
|---------|---------|---------------|-------|
|
|
582
|
+
| **Chrome** | 72+ | Full | All features supported |
|
|
583
|
+
| **Edge** | 79+ | Full | Chromium-based, full support |
|
|
584
|
+
| **Firefox** | 66+ | Full | System audio may require flag |
|
|
585
|
+
| **Safari** | 16.4+ | Partial | No system audio, limited codec |
|
|
586
|
+
| **Opera** | 60+ | Full | Chromium-based |
|
|
587
|
+
| **iOS Safari** | - | None | getDisplayMedia not supported |
|
|
588
|
+
| **Android Chrome** | - | None | getDisplayMedia not supported |
|
|
589
|
+
|
|
590
|
+
> **Note:** Screen recording is a desktop-only feature due to browser API limitations. The component will show an error message on unsupported browsers.
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## Error Handling
|
|
595
|
+
|
|
596
|
+
The component handles various error scenarios:
|
|
597
|
+
|
|
598
|
+
| Error Code | Description | User Action |
|
|
599
|
+
|------------|-------------|-------------|
|
|
600
|
+
| `PERMISSION_DENIED` | User denied screen share | Click "Try Again" |
|
|
601
|
+
| `NOT_SUPPORTED` | Browser doesn't support API | Use Chrome/Edge/Firefox |
|
|
602
|
+
| `MEDIA_RECORDER_ERROR` | Recording failed | Try again |
|
|
603
|
+
| `STREAM_ENDED` | User stopped via browser | Recording saved if data available |
|
|
604
|
+
| `NO_STREAM` | No stream available | Start new recording |
|
|
605
|
+
| `ENCODING_ERROR` | Failed to encode video | Try different quality setting |
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Styling
|
|
610
|
+
|
|
611
|
+
**Styles are automatically injected** when you import the component. No additional CSS imports or Tailwind configuration required!
|
|
612
|
+
|
|
613
|
+
If you prefer to import styles manually (e.g., for SSR or custom loading), you can use:
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
import "@usefy/screen-recorder/styles.css";
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## License
|
|
622
|
+
|
|
623
|
+
MIT © [mirunamu](https://github.com/mirunamu00)
|
|
624
|
+
|
|
625
|
+
This package is part of the [usefy](https://github.com/mirunamu00/usefy) monorepo.
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
<p align="center">
|
|
630
|
+
<sub>Built with care by the usefy team</sub>
|
|
631
|
+
</p>
|