search
React

React Webcam: Capture Photos and Record Videos in Browser

Complete guide to using webcam in React. Take photos, record videos, download captures, and handle camera permissions with working examples.

person By Gautam Sharma
calendar_today December 31, 2024
schedule 15 min read
React Webcam Video Browser API Media

Access webcam in React to capture photos and record videos. Complete examples with download functionality, camera switching, and error handling.

Install react-webcam

Simple webcam library for React.

npm install react-webcam

Basic Webcam Component

Display webcam feed in browser.

import React, { useRef } from 'react';
import Webcam from 'react-webcam';

function WebcamCapture() {
  const webcamRef = useRef(null);

  return (
    <div>
      <h2>Webcam Preview</h2>
      <Webcam
        ref={webcamRef}
        audio={false}
        width={640}
        height={480}
        screenshotFormat="image/jpeg"
      />
    </div>
  );
}

export default WebcamCapture;

Capture Photo and Download

Take photo and save to device.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';

function PhotoCapture() {
  const webcamRef = useRef(null);
  const [capturedImage, setCapturedImage] = useState(null);

  const capturePhoto = () => {
    const imageSrc = webcamRef.current.getScreenshot();
    setCapturedImage(imageSrc);
  };

  const downloadPhoto = () => {
    if (!capturedImage) return;

    const link = document.createElement('a');
    link.href = capturedImage;
    link.download = `photo-${Date.now()}.jpg`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  return (
    <div className="photo-capture">
      <div className="camera-container">
        <Webcam
          ref={webcamRef}
          audio={false}
          width={640}
          height={480}
          screenshotFormat="image/jpeg"
          videoConstraints={{
            width: 1280,
            height: 720,
            facingMode: "user"
          }}
        />
      </div>

      <div className="controls">
        <button onClick={capturePhoto}>
          Capture Photo
        </button>
        <button onClick={downloadPhoto} disabled={!capturedImage}>
          Download Photo
        </button>
      </div>

      {capturedImage && (
        <div className="preview">
          <h3>Captured Photo</h3>
          <img src={capturedImage} alt="Captured" />
        </div>
      )}
    </div>
  );
}

export default PhotoCapture;

Record Video and Download

Record video from webcam.

npm install react-webcam recordrtc
import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';
import RecordRTC from 'recordrtc';

function VideoRecorder() {
  const webcamRef = useRef(null);
  const mediaRecorderRef = useRef(null);
  const [recording, setRecording] = useState(false);
  const [recordedChunks, setRecordedChunks] = useState([]);
  const [videoUrl, setVideoUrl] = useState(null);

  const startRecording = () => {
    setRecording(true);
    setRecordedChunks([]);
    setVideoUrl(null);

    const stream = webcamRef.current.stream;

    mediaRecorderRef.current = new RecordRTC(stream, {
      type: 'video',
      mimeType: 'video/webm',
    });

    mediaRecorderRef.current.startRecording();
  };

  const stopRecording = () => {
    setRecording(false);

    mediaRecorderRef.current.stopRecording(() => {
      const blob = mediaRecorderRef.current.getBlob();
      const url = URL.createObjectURL(blob);
      setVideoUrl(url);
    });
  };

  const downloadVideo = () => {
    if (!videoUrl) return;

    const link = document.createElement('a');
    link.href = videoUrl;
    link.download = `video-${Date.now()}.webm`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  return (
    <div className="video-recorder">
      <div className="camera-container">
        <Webcam
          ref={webcamRef}
          audio={true}
          width={640}
          height={480}
          videoConstraints={{
            width: 1280,
            height: 720,
            facingMode: "user"
          }}
        />
        {recording && (
          <div className="recording-indicator">
            🔴 Recording...
          </div>
        )}
      </div>

      <div className="controls">
        {!recording ? (
          <button onClick={startRecording}>
            Start Recording
          </button>
        ) : (
          <button onClick={stopRecording}>
            Stop Recording
          </button>
        )}
        <button onClick={downloadVideo} disabled={!videoUrl}>
          Download Video
        </button>
      </div>

      {videoUrl && (
        <div className="preview">
          <h3>Recorded Video</h3>
          <video src={videoUrl} controls width={640} />
        </div>
      )}
    </div>
  );
}

export default VideoRecorder;

Complete App with Photo and Video

All-in-one camera app.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';
import RecordRTC from 'recordrtc';
import './CameraApp.css';

function CameraApp() {
  const webcamRef = useRef(null);
  const mediaRecorderRef = useRef(null);

  const [mode, setMode] = useState('photo');
  const [recording, setRecording] = useState(false);
  const [capturedImage, setCapturedImage] = useState(null);
  const [videoUrl, setVideoUrl] = useState(null);
  const [cameraError, setCameraError] = useState(null);

  const capturePhoto = () => {
    const imageSrc = webcamRef.current.getScreenshot();
    setCapturedImage(imageSrc);
  };

  const downloadPhoto = () => {
    if (!capturedImage) return;

    const link = document.createElement('a');
    link.href = capturedImage;
    link.download = `photo-${Date.now()}.jpg`;
    link.click();
  };

  const startRecording = () => {
    setRecording(true);
    setVideoUrl(null);

    const stream = webcamRef.current.stream;

    mediaRecorderRef.current = new RecordRTC(stream, {
      type: 'video',
      mimeType: 'video/webm',
    });

    mediaRecorderRef.current.startRecording();
  };

  const stopRecording = () => {
    setRecording(false);

    mediaRecorderRef.current.stopRecording(() => {
      const blob = mediaRecorderRef.current.getBlob();
      const url = URL.createObjectURL(blob);
      setVideoUrl(url);
    });
  };

  const downloadVideo = () => {
    if (!videoUrl) return;

    const link = document.createElement('a');
    link.href = videoUrl;
    link.download = `video-${Date.now()}.webm`;
    link.click();
  };

  const handleUserMediaError = (error) => {
    console.error('Camera error:', error);
    setCameraError('Failed to access camera. Please check permissions.');
  };

  return (
    <div className="camera-app">
      <header>
        <h1>Camera App</h1>
        <div className="mode-switch">
          <button
            className={mode === 'photo' ? 'active' : ''}
            onClick={() => setMode('photo')}
          >
            Photo
          </button>
          <button
            className={mode === 'video' ? 'active' : ''}
            onClick={() => setMode('video')}
          >
            Video
          </button>
        </div>
      </header>

      <main>
        {cameraError ? (
          <div className="error">{cameraError}</div>
        ) : (
          <div className="camera-view">
            <Webcam
              ref={webcamRef}
              audio={mode === 'video'}
              width={640}
              height={480}
              screenshotFormat="image/jpeg"
              videoConstraints={{
                width: 1280,
                height: 720,
                facingMode: "user"
              }}
              onUserMediaError={handleUserMediaError}
            />
            {recording && (
              <div className="recording-badge">
                🔴 REC
              </div>
            )}
          </div>
        )}

        <div className="controls">
          {mode === 'photo' ? (
            <>
              <button
                className="capture-btn"
                onClick={capturePhoto}
              >
                📷 Capture
              </button>
              <button
                onClick={downloadPhoto}
                disabled={!capturedImage}
              >
                Download Photo
              </button>
            </>
          ) : (
            <>
              {!recording ? (
                <button
                  className="record-btn"
                  onClick={startRecording}
                >
                  ⏺ Start Recording
                </button>
              ) : (
                <button
                  className="stop-btn"
                  onClick={stopRecording}
                >
                  ⏹ Stop
                </button>
              )}
              <button
                onClick={downloadVideo}
                disabled={!videoUrl}
              >
                Download Video
              </button>
            </>
          )}
        </div>

        {capturedImage && mode === 'photo' && (
          <div className="preview">
            <h3>Last Captured Photo</h3>
            <img src={capturedImage} alt="Captured" />
          </div>
        )}

        {videoUrl && mode === 'video' && (
          <div className="preview">
            <h3>Last Recorded Video</h3>
            <video src={videoUrl} controls />
          </div>
        )}
      </main>
    </div>
  );
}

export default CameraApp;

CameraApp.css:

.camera-app {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  font-family: system-ui, -apple-system, sans-serif;
}

header {
  margin-bottom: 30px;
}

header h1 {
  margin: 0 0 20px 0;
  font-size: 32px;
}

.mode-switch {
  display: flex;
  gap: 10px;
}

.mode-switch button {
  padding: 10px 20px;
  border: 2px solid #333;
  background: white;
  border-radius: 8px;
  cursor: pointer;
  font-size: 16px;
  transition: all 0.2s;
}

.mode-switch button.active {
  background: #333;
  color: white;
}

.camera-view {
  position: relative;
  margin-bottom: 20px;
  border-radius: 12px;
  overflow: hidden;
  background: #000;
}

.camera-view video {
  width: 100%;
  height: auto;
  display: block;
}

.recording-badge {
  position: absolute;
  top: 20px;
  right: 20px;
  background: rgba(255, 0, 0, 0.9);
  color: white;
  padding: 8px 16px;
  border-radius: 20px;
  font-weight: bold;
  animation: pulse 1s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.controls {
  display: flex;
  gap: 10px;
  margin-bottom: 30px;
}

.controls button {
  flex: 1;
  padding: 15px;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}

.capture-btn,
.record-btn {
  background: #007bff;
  color: white;
}

.capture-btn:hover,
.record-btn:hover {
  background: #0056b3;
}

.stop-btn {
  background: #dc3545;
  color: white;
}

.stop-btn:hover {
  background: #a71d2a;
}

.controls button:disabled {
  background: #ccc;
  cursor: not-allowed;
}

.preview {
  margin-top: 30px;
  padding: 20px;
  background: #f5f5f5;
  border-radius: 12px;
}

.preview h3 {
  margin: 0 0 15px 0;
}

.preview img,
.preview video {
  width: 100%;
  height: auto;
  border-radius: 8px;
}

.error {
  padding: 20px;
  background: #fee;
  border: 2px solid #fcc;
  border-radius: 8px;
  color: #c33;
  text-align: center;
}

Switch Front and Back Camera

Toggle between cameras on mobile.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';

function CameraSwitch() {
  const webcamRef = useRef(null);
  const [facingMode, setFacingMode] = useState('user');
  const [capturedImage, setCapturedImage] = useState(null);

  const switchCamera = () => {
    setFacingMode(prev => prev === 'user' ? 'environment' : 'user');
  };

  const capturePhoto = () => {
    const imageSrc = webcamRef.current.getScreenshot();
    setCapturedImage(imageSrc);
  };

  return (
    <div>
      <Webcam
        ref={webcamRef}
        audio={false}
        screenshotFormat="image/jpeg"
        videoConstraints={{
          facingMode: facingMode
        }}
      />

      <div className="controls">
        <button onClick={capturePhoto}>
          Capture
        </button>
        <button onClick={switchCamera}>
          Switch Camera
        </button>
      </div>

      {capturedImage && (
        <img src={capturedImage} alt="Captured" />
      )}
    </div>
  );
}

export default CameraSwitch;

Native MediaDevices API

Webcam without external library.

import React, { useRef, useEffect, useState } from 'react';

function NativeWebcam() {
  const videoRef = useRef(null);
  const canvasRef = useRef(null);
  const [stream, setStream] = useState(null);
  const [capturedImage, setCapturedImage] = useState(null);

  useEffect(() => {
    startCamera();
    return () => stopCamera();
  }, []);

  const startCamera = async () => {
    try {
      const mediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          width: 1280,
          height: 720,
          facingMode: 'user'
        },
        audio: false
      });

      videoRef.current.srcObject = mediaStream;
      setStream(mediaStream);
    } catch (error) {
      console.error('Error accessing camera:', error);
      alert('Failed to access camera');
    }
  };

  const stopCamera = () => {
    if (stream) {
      stream.getTracks().forEach(track => track.stop());
    }
  };

  const capturePhoto = () => {
    const video = videoRef.current;
    const canvas = canvasRef.current;

    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

    const imageDataUrl = canvas.toDataURL('image/jpeg');
    setCapturedImage(imageDataUrl);
  };

  const downloadPhoto = () => {
    if (!capturedImage) return;

    const link = document.createElement('a');
    link.href = capturedImage;
    link.download = `photo-${Date.now()}.jpg`;
    link.click();
  };

  return (
    <div>
      <video
        ref={videoRef}
        autoPlay
        playsInline
        style={{ width: '100%', maxWidth: '640px' }}
      />

      <canvas ref={canvasRef} style={{ display: 'none' }} />

      <div className="controls">
        <button onClick={capturePhoto}>
          Capture Photo
        </button>
        <button onClick={downloadPhoto} disabled={!capturedImage}>
          Download
        </button>
      </div>

      {capturedImage && (
        <div>
          <h3>Captured Photo</h3>
          <img src={capturedImage} alt="Captured" style={{ maxWidth: '100%' }} />
        </div>
      )}
    </div>
  );
}

export default NativeWebcam;

Record Video with MediaRecorder

Native video recording.

import React, { useRef, useEffect, useState } from 'react';

function NativeVideoRecorder() {
  const videoRef = useRef(null);
  const mediaRecorderRef = useRef(null);
  const [stream, setStream] = useState(null);
  const [recording, setRecording] = useState(false);
  const [videoUrl, setVideoUrl] = useState(null);
  const [recordedChunks, setRecordedChunks] = useState([]);

  useEffect(() => {
    startCamera();
    return () => stopCamera();
  }, []);

  const startCamera = async () => {
    try {
      const mediaStream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true
      });

      videoRef.current.srcObject = mediaStream;
      setStream(mediaStream);
    } catch (error) {
      console.error('Error accessing camera:', error);
    }
  };

  const stopCamera = () => {
    if (stream) {
      stream.getTracks().forEach(track => track.stop());
    }
  };

  const startRecording = () => {
    setRecordedChunks([]);
    setVideoUrl(null);

    const options = {
      mimeType: 'video/webm;codecs=vp9',
      videoBitsPerSecond: 2500000
    };

    try {
      mediaRecorderRef.current = new MediaRecorder(stream, options);
    } catch (error) {
      mediaRecorderRef.current = new MediaRecorder(stream);
    }

    mediaRecorderRef.current.ondataavailable = (event) => {
      if (event.data.size > 0) {
        setRecordedChunks(prev => [...prev, event.data]);
      }
    };

    mediaRecorderRef.current.onstop = () => {
      const blob = new Blob(recordedChunks, { type: 'video/webm' });
      const url = URL.createObjectURL(blob);
      setVideoUrl(url);
    };

    mediaRecorderRef.current.start();
    setRecording(true);
  };

  const stopRecording = () => {
    mediaRecorderRef.current.stop();
    setRecording(false);
  };

  const downloadVideo = () => {
    if (!videoUrl) return;

    const link = document.createElement('a');
    link.href = videoUrl;
    link.download = `video-${Date.now()}.webm`;
    link.click();
  };

  return (
    <div>
      <video
        ref={videoRef}
        autoPlay
        playsInline
        muted
        style={{ width: '100%', maxWidth: '640px' }}
      />

      {recording && (
        <div style={{ color: 'red', fontWeight: 'bold' }}>
          🔴 Recording...
        </div>
      )}

      <div className="controls">
        {!recording ? (
          <button onClick={startRecording}>
            Start Recording
          </button>
        ) : (
          <button onClick={stopRecording}>
            Stop Recording
          </button>
        )}
        <button onClick={downloadVideo} disabled={!videoUrl}>
          Download Video
        </button>
      </div>

      {videoUrl && (
        <div>
          <h3>Recorded Video</h3>
          <video src={videoUrl} controls style={{ maxWidth: '100%' }} />
        </div>
      )}
    </div>
  );
}

export default NativeVideoRecorder;

Take Multiple Photos

Photo gallery with multiple captures.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';

function PhotoGallery() {
  const webcamRef = useRef(null);
  const [photos, setPhotos] = useState([]);

  const capturePhoto = () => {
    const imageSrc = webcamRef.current.getScreenshot();
    setPhotos(prev => [...prev, {
      id: Date.now(),
      src: imageSrc,
      timestamp: new Date().toLocaleString()
    }]);
  };

  const deletePhoto = (id) => {
    setPhotos(prev => prev.filter(photo => photo.id !== id));
  };

  const downloadPhoto = (photo) => {
    const link = document.createElement('a');
    link.href = photo.src;
    link.download = `photo-${photo.id}.jpg`;
    link.click();
  };

  const downloadAll = () => {
    photos.forEach((photo, index) => {
      setTimeout(() => {
        downloadPhoto(photo);
      }, index * 100);
    });
  };

  return (
    <div className="photo-gallery">
      <div className="camera">
        <Webcam
          ref={webcamRef}
          audio={false}
          screenshotFormat="image/jpeg"
          width={640}
          height={480}
        />
        <button onClick={capturePhoto} className="capture-btn">
          📷 Capture ({photos.length})
        </button>
      </div>

      <div className="controls">
        <button onClick={downloadAll} disabled={photos.length === 0}>
          Download All ({photos.length})
        </button>
        <button onClick={() => setPhotos([])} disabled={photos.length === 0}>
          Clear All
        </button>
      </div>

      <div className="gallery">
        {photos.map(photo => (
          <div key={photo.id} className="photo-card">
            <img src={photo.src} alt={`Capture ${photo.id}`} />
            <div className="photo-info">
              <span>{photo.timestamp}</span>
              <div className="photo-actions">
                <button onClick={() => downloadPhoto(photo)}>
                  Download
                </button>
                <button onClick={() => deletePhoto(photo.id)}>
                  Delete
                </button>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

export default PhotoGallery;

Add Photo Filters

Apply filters before capture.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';

function PhotoFilters() {
  const webcamRef = useRef(null);
  const canvasRef = useRef(null);
  const [filter, setFilter] = useState('none');
  const [capturedImage, setCapturedImage] = useState(null);

  const filters = {
    none: 'none',
    grayscale: 'grayscale(100%)',
    sepia: 'sepia(100%)',
    blur: 'blur(3px)',
    brightness: 'brightness(150%)',
    contrast: 'contrast(200%)',
    invert: 'invert(100%)'
  };

  const captureWithFilter = () => {
    const video = webcamRef.current.video;
    const canvas = canvasRef.current;

    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;

    const ctx = canvas.getContext('2d');

    ctx.filter = filters[filter];
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

    const imageDataUrl = canvas.toDataURL('image/jpeg');
    setCapturedImage(imageDataUrl);
  };

  const downloadPhoto = () => {
    const link = document.createElement('a');
    link.href = capturedImage;
    link.download = `photo-${filter}-${Date.now()}.jpg`;
    link.click();
  };

  return (
    <div>
      <div style={{ filter: filters[filter] }}>
        <Webcam
          ref={webcamRef}
          audio={false}
          width={640}
          height={480}
        />
      </div>

      <canvas ref={canvasRef} style={{ display: 'none' }} />

      <div className="filter-controls">
        <label>Filter:</label>
        <select value={filter} onChange={(e) => setFilter(e.target.value)}>
          <option value="none">None</option>
          <option value="grayscale">Grayscale</option>
          <option value="sepia">Sepia</option>
          <option value="blur">Blur</option>
          <option value="brightness">Bright</option>
          <option value="contrast">Contrast</option>
          <option value="invert">Invert</option>
        </select>
      </div>

      <div className="controls">
        <button onClick={captureWithFilter}>
          Capture with {filter} filter
        </button>
        <button onClick={downloadPhoto} disabled={!capturedImage}>
          Download
        </button>
      </div>

      {capturedImage && (
        <div>
          <h3>Captured Photo</h3>
          <img src={capturedImage} alt="Captured" />
        </div>
      )}
    </div>
  );
}

export default PhotoFilters;

Check Camera Permissions

Handle camera access permissions.

import React, { useState, useEffect } from 'react';

function CameraPermissions({ children }) {
  const [permission, setPermission] = useState('prompt');
  const [error, setError] = useState(null);

  useEffect(() => {
    checkPermission();
  }, []);

  const checkPermission = async () => {
    try {
      const result = await navigator.permissions.query({ name: 'camera' });
      setPermission(result.state);

      result.addEventListener('change', () => {
        setPermission(result.state);
      });
    } catch (error) {
      console.log('Permission API not supported');
    }
  };

  const requestPermission = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: false
      });

      setPermission('granted');
      stream.getTracks().forEach(track => track.stop());
    } catch (error) {
      setError('Camera access denied');
      setPermission('denied');
    }
  };

  if (permission === 'denied') {
    return (
      <div className="permission-denied">
        <h2>Camera Access Denied</h2>
        <p>Please enable camera access in your browser settings.</p>
      </div>
    );
  }

  if (permission === 'prompt') {
    return (
      <div className="permission-prompt">
        <h2>Camera Access Required</h2>
        <p>This app needs access to your camera.</p>
        <button onClick={requestPermission}>
          Enable Camera
        </button>
        {error && <p className="error">{error}</p>}
      </div>
    );
  }

  return children;
}

export default CameraPermissions;

Usage:

import CameraPermissions from './CameraPermissions';
import CameraApp from './CameraApp';

function App() {
  return (
    <CameraPermissions>
      <CameraApp />
    </CameraPermissions>
  );
}

Record Video with Timer

Show recording duration.

import React, { useRef, useState, useEffect } from 'react';
import Webcam from 'react-webcam';
import RecordRTC from 'recordrtc';

function TimedVideoRecorder() {
  const webcamRef = useRef(null);
  const mediaRecorderRef = useRef(null);
  const [recording, setRecording] = useState(false);
  const [duration, setDuration] = useState(0);
  const [videoUrl, setVideoUrl] = useState(null);

  useEffect(() => {
    let interval;

    if (recording) {
      interval = setInterval(() => {
        setDuration(prev => prev + 1);
      }, 1000);
    } else {
      setDuration(0);
    }

    return () => clearInterval(interval);
  }, [recording]);

  const formatTime = (seconds) => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  };

  const startRecording = () => {
    setRecording(true);
    setVideoUrl(null);

    const stream = webcamRef.current.stream;

    mediaRecorderRef.current = new RecordRTC(stream, {
      type: 'video',
      mimeType: 'video/webm',
    });

    mediaRecorderRef.current.startRecording();
  };

  const stopRecording = () => {
    setRecording(false);

    mediaRecorderRef.current.stopRecording(() => {
      const blob = mediaRecorderRef.current.getBlob();
      const url = URL.createObjectURL(blob);
      setVideoUrl(url);
    });
  };

  const downloadVideo = () => {
    const link = document.createElement('a');
    link.href = videoUrl;
    link.download = `video-${Date.now()}.webm`;
    link.click();
  };

  return (
    <div>
      <div className="camera-container">
        <Webcam
          ref={webcamRef}
          audio={true}
          width={640}
          height={480}
        />
        {recording && (
          <div className="recording-timer">
            🔴 {formatTime(duration)}
          </div>
        )}
      </div>

      <div className="controls">
        {!recording ? (
          <button onClick={startRecording}>
            Start Recording
          </button>
        ) : (
          <button onClick={stopRecording}>
            Stop ({formatTime(duration)})
          </button>
        )}
        <button onClick={downloadVideo} disabled={!videoUrl}>
          Download
        </button>
      </div>

      {videoUrl && (
        <div>
          <video src={videoUrl} controls />
        </div>
      )}
    </div>
  );
}

export default TimedVideoRecorder;

List Available Cameras

Select from multiple cameras.

import React, { useState, useEffect, useRef } from 'react';
import Webcam from 'react-webcam';

function CameraSelector() {
  const webcamRef = useRef(null);
  const [devices, setDevices] = useState([]);
  const [selectedDevice, setSelectedDevice] = useState(null);

  useEffect(() => {
    getDevices();
  }, []);

  const getDevices = async () => {
    const mediaDevices = await navigator.mediaDevices.enumerateDevices();
    const videoDevices = mediaDevices.filter(
      device => device.kind === 'videoinput'
    );
    setDevices(videoDevices);

    if (videoDevices.length > 0) {
      setSelectedDevice(videoDevices[0].deviceId);
    }
  };

  const capturePhoto = () => {
    const imageSrc = webcamRef.current.getScreenshot();
    const link = document.createElement('a');
    link.href = imageSrc;
    link.download = `photo-${Date.now()}.jpg`;
    link.click();
  };

  return (
    <div>
      <div className="device-selector">
        <label>Select Camera:</label>
        <select
          value={selectedDevice}
          onChange={(e) => setSelectedDevice(e.target.value)}
        >
          {devices.map(device => (
            <option key={device.deviceId} value={device.deviceId}>
              {device.label || `Camera ${device.deviceId.slice(0, 5)}`}
            </option>
          ))}
        </select>
      </div>

      {selectedDevice && (
        <>
          <Webcam
            ref={webcamRef}
            audio={false}
            videoConstraints={{
              deviceId: selectedDevice
            }}
          />
          <button onClick={capturePhoto}>
            Capture Photo
          </button>
        </>
      )}
    </div>
  );
}

export default CameraSelector;

Error Handling

Handle common camera errors.

import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';

function SafeWebcam() {
  const webcamRef = useRef(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  const handleUserMedia = () => {
    setLoading(false);
    setError(null);
  };

  const handleUserMediaError = (error) => {
    setLoading(false);

    let errorMessage = 'Failed to access camera';

    if (error.name === 'NotAllowedError') {
      errorMessage = 'Camera access denied. Please allow camera permissions.';
    } else if (error.name === 'NotFoundError') {
      errorMessage = 'No camera found on this device.';
    } else if (error.name === 'NotReadableError') {
      errorMessage = 'Camera is already in use by another application.';
    } else if (error.name === 'OverconstrainedError') {
      errorMessage = 'Camera constraints could not be satisfied.';
    }

    setError(errorMessage);
  };

  const retryCamera = () => {
    setError(null);
    setLoading(true);
  };

  if (error) {
    return (
      <div className="camera-error">
        <h3>Camera Error</h3>
        <p>{error}</p>
        <button onClick={retryCamera}>
          Try Again
        </button>
      </div>
    );
  }

  return (
    <div>
      {loading && <div>Loading camera...</div>}

      <Webcam
        ref={webcamRef}
        audio={false}
        onUserMedia={handleUserMedia}
        onUserMediaError={handleUserMediaError}
        videoConstraints={{
          width: 1280,
          height: 720,
          facingMode: "user"
        }}
      />
    </div>
  );
}

export default SafeWebcam;

Quick Reference

Install Dependencies:

npm install react-webcam recordrtc

Basic Imports:

import Webcam from 'react-webcam';
import RecordRTC from 'recordrtc';

Capture Photo:

const imageSrc = webcamRef.current.getScreenshot();

Download File:

const link = document.createElement('a');
link.href = imageUrl;
link.download = 'photo.jpg';
link.click();

Video Constraints:

videoConstraints={{
  width: 1280,
  height: 720,
  facingMode: "user"  // or "environment" for back camera
}}

Common Props:

  • audio={true} - Enable microphone
  • screenshotFormat="image/jpeg" - Image format
  • mirrored={true} - Mirror video
  • videoConstraints - Camera settings

Browser Support:

  • Chrome, Firefox, Safari (desktop and mobile)
  • HTTPS required (or localhost)
  • Requires user permission

Best Practices:

  • Always handle errors gracefully
  • Check browser support
  • Request permissions properly
  • Clean up streams on unmount
  • Show loading states
  • Provide clear user feedback
  • Use HTTPS in production

Conclusion

React makes webcam access simple with react-webcam library. Capture photos with getScreenshot(), record videos with RecordRTC, and download using blob URLs. Always handle permissions and errors for better user experience.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

React

FFmpeg.wasm in React: Build a Complete Video Trimmer That Runs in Your Browser

Complete guide to building a professional video trimmer with React and FFmpeg.wasm. Trim, preview, and export videos entirely in the browser with zero backend.

December 31, 2024
React

Getting Started with React Hooks in 2025

Learn how to use React Hooks effectively in your modern React applications with practical examples and best practices.

December 15, 2024
React

How to integrate jsPDF Library in React to Edit PDF in Browser

Quick guide to using jsPDF in React applications for creating and editing PDF documents directly in the browser.

December 29, 2024