No articles found
Try different keywords or browse our categories
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.
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 microphonescreenshotFormat="image/jpeg"- Image formatmirrored={true}- Mirror videovideoConstraints- 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.
Related Articles
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.
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.
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.