Vidtreo

React Integration

Complete guide to integrating VIDTREO in React applications with components and hooks.

React Integration

The @vidtreo/recorder-react package provides a ready-to-use React component and a powerful hook for custom implementations.

Installation

npm install @vidtreo/recorder-react

Quick Start

The simplest way to add video recording is with the VidtreoRecorder component:

import { VidtreoRecorder } from '@vidtreo/recorder-react';

function App() {
  return (
    <VidtreoRecorder
      apiKey={import.meta.env.VITE_VIDTREO_API_KEY}
      onUploadComplete={(result) => {
        console.log('Video ready:', result.uploadUrl);
      }}
    />
  );
}

Component Props

Required

PropTypeDescription
apiKeystringYour VIDTREO API key

Optional

PropTypeDefaultDescription
backendUrlstringhttps://core.vidtreo.comAPI endpoint URL
enableSourceSwitchingbooleantrueAllow camera/screen switching
enableMutebooleantrueShow mute button
enablePausebooleantrueShow pause/resume button
enableDeviceChangebooleantrueAllow device selection
maxRecordingTimenumberundefinedMax duration in milliseconds
countdownDurationnumber3000Pre-recording countdown in ms
userMetadataobjectundefinedCustom metadata for the video
langstring"en"UI language (en, es, pt)
textsobjectundefinedCustom text overrides
demobooleanfalseDemo mode (no upload)

Callbacks

CallbackParametersDescription
onRecordingStart()Fired when recording begins
onRecordingStop()Fired when recording ends
onUploadProgress(progress: number)Upload progress (0-1)
onUploadComplete(result)Upload finished successfully
onUploadError(error: Error)Upload failed
onError(error: Error)Any error occurred

Complete Example

import { VidtreoRecorder } from '@vidtreo/recorder-react';
import { useState } from 'react';

function VideoRecorderPage() {
  const [videoUrl, setVideoUrl] = useState<string | null>(null);
  const [uploadProgress, setUploadProgress] = useState(0);
  const [isRecording, setIsRecording] = useState(false);

  return (
    <div className="container">
      <h1>Record a Video Message</h1>

      <VidtreoRecorder
        apiKey={import.meta.env.VITE_VIDTREO_API_KEY}
        enableSourceSwitching={true}
        enableMute={true}
        enablePause={true}
        enableDeviceChange={true}
        maxRecordingTime={120000} // 2 minutes
        countdownDuration={3000}
        lang="en"
        userMetadata={{
          userId: 'user-123',
          sessionId: 'session-456',
        }}
        onRecordingStart={() => {
          setIsRecording(true);
          setVideoUrl(null);
        }}
        onRecordingStop={() => {
          setIsRecording(false);
        }}
        onUploadProgress={(progress) => {
          setUploadProgress(progress);
        }}
        onUploadComplete={(result) => {
          setVideoUrl(result.uploadUrl);
          setUploadProgress(0);
        }}
        onError={(error) => {
          console.error('Error:', error);
        }}
      />

      {uploadProgress > 0 && uploadProgress < 1 && (
        <div className="progress-bar">
          <div style={{ width: `${uploadProgress * 100}%` }} />
          <span>{Math.round(uploadProgress * 100)}%</span>
        </div>
      )}

      {videoUrl && (
        <div className="result">
          <h2>Recording Complete!</h2>
          <video src={videoUrl} controls />
        </div>
      )}
    </div>
  );
}

Using the Hook

For full control over the UI, use the useVidtreoRecorder hook:

import { useVidtreoRecorder } from '@vidtreo/recorder-react';
import { useRef, useEffect } from 'react';

function CustomRecorder() {
  const videoRef = useRef<HTMLVideoElement>(null);

  const { state, actions, audioLevel } = useVidtreoRecorder({
    apiKey: import.meta.env.VITE_VIDTREO_API_KEY,
    onUploadComplete: (result) => {
      console.log('Video URL:', result.uploadUrl);
    },
  });

  // Attach stream to video element
  useEffect(() => {
    if (videoRef.current && state.stream) {
      videoRef.current.srcObject = state.stream;
    }
  }, [state.stream]);

  // Initialize on mount
  useEffect(() => {
    actions.startPreview();
  }, []);

  return (
    <div className="custom-recorder">
      <video ref={videoRef} autoPlay muted playsInline />

      {/* Audio level indicator */}
      <div className="audio-meter" style={{ width: `${audioLevel * 100}%` }} />

      {/* Recording controls */}
      <div className="controls">
        {state.recordingState === 'idle' && (
          <button onClick={() => actions.startRecording()}>
            Start Recording
          </button>
        )}

        {state.recordingState === 'countdown' && (
          <div className="countdown">{state.countdown}</div>
        )}

        {state.recordingState === 'recording' && (
          <>
            <span className="timer">{state.timer}</span>

            <button onClick={() => actions.toggleMute()}>
              {state.isMuted ? 'Unmute' : 'Mute'}
            </button>

            <button onClick={() =>
              state.isPaused ? actions.resumeRecording() : actions.pauseRecording()
            }>
              {state.isPaused ? 'Resume' : 'Pause'}
            </button>

            <button onClick={() => actions.stopRecording()}>
              Stop
            </button>
          </>
        )}
      </div>

      {/* Device selection */}
      <div className="devices">
        <select
          value={state.devices.selectedCamera || ''}
          onChange={(e) => actions.changeCamera(e.target.value)}
        >
          {state.devices.cameras.map((camera) => (
            <option key={camera.deviceId} value={camera.deviceId}>
              {camera.label}
            </option>
          ))}
        </select>

        <select
          value={state.devices.selectedMic || ''}
          onChange={(e) => actions.changeMic(e.target.value)}
        >
          {state.devices.microphones.map((mic) => (
            <option key={mic.deviceId} value={mic.deviceId}>
              {mic.label}
            </option>
          ))}
        </select>
      </div>

      {/* Upload progress */}
      {state.uploadProgress !== null && (
        <div className="upload-progress">
          Uploading: {Math.round(state.uploadProgress * 100)}%
        </div>
      )}

      {state.error && (
        <div className="error">{state.error}</div>
      )}
    </div>
  );
}

Hook Return Values

State Object

interface State {
  recordingState: 'idle' | 'countdown' | 'recording';
  stream: MediaStream | null;
  isMuted: boolean;
  isPaused: boolean;
  error: string | null;
  countdown: number | null;
  timer: string;                    // "00:00" format
  uploadProgress: number | null;    // 0-1
  transitionMessage: string | null;
  isInitialized: boolean;
  recordedBlob: Blob | null;
  devices: {
    cameras: MediaDeviceInfo[];
    microphones: MediaDeviceInfo[];
    selectedCamera: string | null;
    selectedMic: string | null;
  };
}

Actions Object

interface Actions {
  startRecording(sourceType?: 'camera' | 'screen'): Promise<void>;
  stopRecording(): Promise<RecordingStopResult>;
  pauseRecording(): void;
  resumeRecording(): void;
  toggleMute(): void;
  switchSource(sourceType: 'camera' | 'screen'): Promise<void>;
  changeCamera(deviceId: string): Promise<void>;
  changeMic(deviceId: string): Promise<void>;
  startPreview(sourceType?: 'camera' | 'screen'): Promise<void>;
  downloadVideo(): void;
  cleanup(): void;
}

Internationalization

Built-in Languages

// English (default)
<VidtreoRecorder apiKey="..." lang="en" />

// Spanish
<VidtreoRecorder apiKey="..." lang="es" />

// Portuguese
<VidtreoRecorder apiKey="..." lang="pt" />

Custom Texts

Override any UI text:

<VidtreoRecorder
  apiKey="..."
  lang="en"
  texts={{
    record: 'Start Capture',
    stop: 'End Capture',
    pause: 'Hold',
    resume: 'Continue',
    mute: 'Silence',
    unmute: 'Sound On',
    switchSource: 'Toggle Source',
    uploading: 'Sending...',
  }}
/>

Available Text Keys

KeyDefault (English)
initializingCameraInitializing camera...
grantPermissionsPlease grant camera permissions
switchingDeviceSwitching device...
recordingStartsInRecording starts in
switchingSourceSwitching source...
recREC
settingsSettings
recordRecord
stopStop
pausePause
resumeResume
muteMute
unmuteUnmute
switchSourceSwitch Source
cameraCamera
microphoneMicrophone
uploadingUploading...

TypeScript Support

All types are exported:

import type {
  VidtreoRecorderProps,
  UseVidtreoRecorderConfig,
  UseVidtreoRecorderReturn,
  VidtreoRecorderState,
  VidtreoRecorderActions,
  PartialTranslations,
  TranslationKeys,
} from '@vidtreo/recorder-react';

Next.js Compatibility

For Next.js with App Router, ensure client-side rendering:

'use client';

import { VidtreoRecorder } from '@vidtreo/recorder-react';

export default function RecorderPage() {
  return (
    <VidtreoRecorder
      apiKey={process.env.NEXT_PUBLIC_VIDTREO_API_KEY!}
      onUploadComplete={(result) => {
        console.log('Video:', result.uploadUrl);
      }}
    />
  );
}

The VIDTREO recorder requires browser APIs and must render on the client side.

Next Steps

On this page