@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 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>