Hooks
No hooks found in any category.
useUserMedia
sensorsInstallation
npx usehooks-cli@latest add use-user-media
Description
A React hook for accessing user media devices (camera and microphone) using the getUserMedia API. Provides comprehensive error handling and stream management.
Parameters
Name | Type | Default | Description |
---|---|---|---|
initialConstraints? | UseUserMediaConstraints | - | Initial media constraints for video and audio |
Parameter Properties
initialConstraints properties:
Name | Type | Description |
---|---|---|
video? | boolean | MediaTrackConstraints | Video constraints or boolean to enable/disable video |
audio? | boolean | MediaTrackConstraints | Audio constraints or boolean to enable/disable audio |
Return Type
UseUserMediaReturn
Property | Type | Description |
---|---|---|
stream | MediaStream | null | Current media stream from user devices |
error | string | null | Error message if media access failed |
isLoading | boolean | Whether currently requesting media access |
isSupported | boolean | Whether getUserMedia is supported in the browser |
startCapture | (constraints?: UseUserMediaConstraints) => Promise<void> | Function to start media capture with optional constraints |
stopCapture | () => void | Function to stop media capture and release devices |
Examples
Video chat component
Basic video chat setup with camera and microphone
1import { useUserMedia } from '@usehooks.io/use-user-media';
2import { useRef, useEffect } from 'react';
3
4function VideoChat() {
5 const videoRef = useRef<HTMLVideoElement>(null);
6 const {
7 stream,
8 error,
9 isLoading,
10 isSupported,
11 startCapture,
12 stopCapture
13 } = useUserMedia({ video: true, audio: true });
14
15 useEffect(() => {
16 if (videoRef.current && stream) {
17 videoRef.current.srcObject = stream;
18 }
19 }, [stream]);
20
21 if (!isSupported) {
22 return <div>getUserMedia not supported</div>;
23 }
24
25 return (
26 <div>
27 <video ref={videoRef} autoPlay muted />
28
29 {error && <p style={{ color: 'red' }}>Error: {error}</p>}
30
31 <button onClick={() => startCapture()} disabled={isLoading}>
32 {isLoading ? 'Starting...' : 'Start Camera'}
33 </button>
34
35 <button onClick={stopCapture}>
36 Stop Camera
37 </button>
38 </div>
39 );
40}
Dependencies
react
Notes
- •Automatically stops previous streams when starting new capture
- •Provides detailed error messages for different failure scenarios
- •Requires HTTPS in production for security reasons
- •Automatically cleans up streams on component unmount
- •Supports both boolean and detailed MediaTrackConstraints
Implementation
1'use client';
2
3import { useState, useCallback, useRef, useEffect } from "react";
4
5interface UseUserMediaConstraints {
6 video?: boolean | MediaTrackConstraints;
7 audio?: boolean | MediaTrackConstraints;
8}
9
10interface UseUserMediaReturn {
11 stream: MediaStream | null;
12 error: string | null;
13 isLoading: boolean;
14 isSupported: boolean;
15 startCapture: (constraints?: UseUserMediaConstraints) => Promise<void>;
16 stopCapture: () => void;
17}
18
19const DEFAULT_CONSTRAINTS: UseUserMediaConstraints = {
20 video: true,
21 audio: true,
22};
23
24export const useUserMedia = (
25 initialConstraints: UseUserMediaConstraints = DEFAULT_CONSTRAINTS
26): UseUserMediaReturn => {
27 const [stream, setStream] = useState<MediaStream | null>(null);
28 const [error, setError] = useState<string | null>(null);
29 const [isLoading, setIsLoading] = useState(false);
30 const streamRef = useRef<MediaStream | null>(null);
31
32 const isSupported =
33 typeof navigator !== "undefined" &&
34 "mediaDevices" in navigator &&
35 "getUserMedia" in navigator.mediaDevices;
36
37 const stopCapture = useCallback(() => {
38 if (streamRef.current) {
39 streamRef.current.getTracks().forEach((track) => {
40 track.stop();
41 });
42 streamRef.current = null;
43 setStream(null);
44 }
45 setError(null);
46 }, []);
47
48 const startCapture = useCallback(
49 async (constraints: UseUserMediaConstraints = initialConstraints) => {
50 if (!isSupported) {
51 setError("getUserMedia is not supported in this browser");
52 return;
53 }
54
55 // Stop any existing stream
56 stopCapture();
57
58 setIsLoading(true);
59 setError(null);
60
61 try {
62 const mediaStream =
63 await navigator.mediaDevices.getUserMedia(constraints);
64 streamRef.current = mediaStream;
65 setStream(mediaStream);
66 } catch (err) {
67 let errorMessage = "Failed to access media devices";
68
69 if (err instanceof Error) {
70 switch (err.name) {
71 case "NotAllowedError":
72 errorMessage =
73 "Permission denied. Please allow camera/microphone access.";
74 break;
75 case "NotFoundError":
76 errorMessage = "No camera or microphone found.";
77 break;
78 case "NotReadableError":
79 errorMessage = "Camera or microphone is already in use.";
80 break;
81 case "OverconstrainedError":
82 errorMessage =
83 "Camera or microphone constraints cannot be satisfied.";
84 break;
85 case "SecurityError":
86 errorMessage = "Security error. Make sure you're using HTTPS.";
87 break;
88 case "AbortError":
89 errorMessage = "Media access was aborted.";
90 break;
91 default:
92 errorMessage = err.message || errorMessage;
93 }
94 }
95
96 setError(errorMessage);
97 } finally {
98 setIsLoading(false);
99 }
100 },
101 [isSupported, initialConstraints, stopCapture]
102 );
103
104 // Cleanup on unmount
105 useEffect(() => {
106 return () => {
107 stopCapture();
108 };
109 }, [stopCapture]);
110
111 return {
112 stream,
113 error,
114 isLoading,
115 isSupported,
116 startCapture,
117 stopCapture,
118 };
119};
120
121export default useUserMedia;
122