@vidtreo/recorder-react
v1.17.0
Published
React components and hooks for @vidtreo/recorder - video recording SDK
Readme
@vidtreo/recorder-react
React components and hooks for the Vidtreo video recording SDK. This package provides a ready-to-use React component and a flexible hook for building custom recording interfaces.
Installation
npm install @vidtreo/recorder-reactPeer Dependencies:
@vidtreo/recorder >=1.9.0react >=18.0.0react-dom >=18.0.0
Quick Start
Using the Component (2 lines of code)
import VidtreoRecorder from '@vidtreo/recorder-react';
<VidtreoRecorder
apiKey="your-api-key"
/>Using Environment Variables
Set VIDTREO_API_KEY in your environment:
# .env
VIDTREO_API_KEY=your-api-keyimport VidtreoRecorder from '@vidtreo/recorder-react';
<VidtreoRecorder />Component API
Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| apiKey | string | No* | - | API key for authentication. Required if VIDTREO_API_KEY env var not set and getAccessToken is not used. |
| getAccessToken | () => Promise<string> | No* | - | Returns a short-lived desktop access token minted by your server. Use instead of apiKey (exactly one of the two; passing both shows a configuration error, see Credentials). Called again whenever a fresh token is needed, including when a queued upload resumes after a reload. |
| audioOnly | boolean | No | false | Record audio from the microphone only. See Audio-Only Recording |
| countdownDuration | number | No | - | Countdown duration in milliseconds before recording starts |
| minTimeRecord | number | No | - | Minimum recording time in milliseconds before stop is allowed. Must not be greater than maxRecordingTime when both are positive. |
| maxRecordingTime | number | No | - | Maximum recording time in milliseconds |
| userMetadata | Record<string, unknown> | No | - | Custom metadata to attach to recordings |
| integrity | IntegrityOptions | No | - | { enabled: true, policy?: "report" \| "warn" \| "block" }: collect integrity signals while recording and send them with the upload as integrity; onIntegritySignal receives each signal as it is observed. See Integrity signals |
| onIntegrityBlocked | (reason: IntegrityBlockedReason) => void | No | - | Called when policy: "block" refuses to start a recording because the active camera matches a virtual camera pattern. The start error (code: "integrity.blocked") also reaches onError |
| onIntegrityChallenge | (prompt: IntegrityChallengePrompt) => void | No | - | Called when a liveness challenge is due (integrity.challenges.enabled); the component shows the instruction itself. challenges.lang defaults to lang |
| onIntegrityChallengeEnd | (id: string) => void | No | - | Called when a challenge window ends |
| recordingPrompt | RecordingPrompt | No | - | Prompt shown during recording. Use countdown: { show, placement: "above-countdown" } to float it over the countdown. A label field is still accepted for backward compatibility but is not displayed. |
| enableSourceSwitching | boolean | No | true | Enable switching between camera and screen |
| enableMute | boolean | No | true | Enable mute/unmute functionality |
| enablePause | boolean | No | true | Enable pause/resume functionality |
| enableDeviceChange | boolean | No | true | Enable camera/microphone selection |
| enableTabVisibilityOverlay | boolean | No | false | Show overlay in camera recordings when user switches to another tab |
| tabVisibilityOverlayText | string | No | localized userInAnotherTab text | Custom text rendered for tab visibility overlay |
| nativeCamera | boolean | No | true | Use native camera UI on mobile devices (set to false to use custom UI) |
| maxFileSize | number | No | 2560 | Maximum file size in megabytes (default: 2.5GB) |
| lang | string | No | "en" | Language code for UI text ("en" or "es") |
| texts | PartialTranslations | No | - | Custom text overrides for localization |
| onUploadComplete | (result: RecordingUploadResult) => void | No | - | Callback when upload completes. Same payload for the desktop/overlay recorder and native camera mode. See Upload Completion Payload |
| onUploadProgress | (progress: number, phase: UploadProgressPhase \| null) => void | No | - | Callback for upload progress (0-1) in every mode. phase is "preparing", "transferring", "finalizing", "completed" or null when not reported yet (the first 0 call) |
| onUploadError | (error: Error) => void | No | - | Callback when upload fails. Use getUploadErrorCode(error) to read the error code |
| onRecordingStart | () => void | No | - | Callback when recording starts |
| onRecordingStop | () => void | No | - | Callback when recording stops (once per recording, when finalization starts) |
| onTranscodingProgress | (progress: number) => void | No | - | Callback for transcoding progress (0-1). While it runs the recorder shows the finishing progress overlay |
| onError | (error: Error) => void | No | - | Callback for any errors, including the credential conflict configuration error |
tabVisibilityOverlayText is optional. When enableTabVisibilityOverlay is enabled and text is omitted, the component
uses a localized default based on lang and texts. Overlay rendering is skipped for screen capture recordings.
Audio-Only Recording
Set audioOnly to record from the microphone only. The camera is never requested and no video preview is rendered. The recorder shows a compact voice-note pill (about 20rem wide, 56px high): a red record/stop button, a live waveform of dots that freezes while paused or uploading, the elapsed time (with / 10:00 when the environment sets a maximum duration) and a microphone button that opens a menu with the microphone selector, plus pause/resume and mute while recording. Status text, permission requests, errors, upload progress (Uploading 42%) and the completion message appear in a small caption below the pill.
<VidtreoRecorder
apiKey="your-api-key"
audioOnly
onUploadComplete={(result) => {
if (result.mediaType === 'audio') {
console.log('Audio uploaded:', result.audioId);
}
}}
/>- Audio recording must be enabled for the environment in the backend. When it is not, the recorder shows "Audio recording is not enabled for this environment" (
audioRecordingDisabled) andonErrorreceives an error whosecodeis"recording.audio-disabled"(ERROR_AUDIO_RECORDING_DISABLEDfrom@vidtreo/recorder). - The microphone selector, mute, pause/resume and stop work as for video, from the pill and its menu. The camera selector, source switching and the tab visibility overlay are not shown.
- The permission flow asks only for the microphone.
- On mobile,
overlayandembedshow the same pill.mobileMode="native"andnativeCameraare ignored and fall back tooverlay, because the native camera only captures video. - Format, bitrate, channels, processing and the maximum duration come from the environment audio settings. The recorder stops automatically at the maximum duration.
- In demo mode audio-only recording works without a backend: it uses the default audio settings (128 kbps mono AAC, or Opus where the browser can't encode AAC, with echo cancellation, noise suppression and auto gain), keeps the take in the browser and, once it stops, shows "Recording ready" (
audioOnlyRecordingReady) with a Download action. The file is saved with its container's extension (.m4afor AAC,.webmfor Opus).
Example with Callbacks
<VidtreoRecorder
apiKey="your-api-key"
onUploadComplete={(result) => {
console.log('Recording uploaded:', result.recordingId);
console.log('View at:', result.uploadUrl);
}}
onUploadProgress={(progress, phase) => {
console.log(`Upload (${phase ?? "starting"}): ${Math.round(progress * 100)}%`);
}}
onError={(error) => {
console.error('Error:', error.message);
}}
/>Temporary Access Tokens
<VidtreoRecorder
getAccessToken={async () => {
const response = await fetch('/api/vidtreo-token');
const { token } = await response.json();
return token;
}}
/>The token is never stored. If an upload is interrupted, the component calls getAccessToken again when the upload resumes.
Pass either apiKey or getAccessToken, not both. The prop types reject both at compile time; at runtime (for example from JavaScript) the component renders the configuration error "Provide either apiKey or getAccessToken, not both" and calls onError once with an error whose code is CONFIG_ERROR_CREDENTIAL_CONFLICT ("config.credential-conflict", from @vidtreo/recorder). Passing neither (outside demo mode, and without VIDTREO_API_KEY in the environment) renders the errorApiKeyRequired configuration error and calls onError once. Demo mode ignores credentials.
Progress Overlays
After you stop a recording the preview shows a progress overlay until the upload finishes:
| Stage | Label key | Progress |
|-------|-----------|----------|
| Finalizing the recording (before transcoding reports progress) | finishing | 0% |
| Transcoding | finishing | transcoding progress |
| Uploading | uploading, finalizing when the backend is finalizing, retryingUpload while a retry is pending | upload progress |
The same overlays show in the mobile overlay recorder. In native camera mode a resumed upload without a preview shows a standalone progress bar with the same labels.
Upload Completion Payload
onUploadComplete receives a RecordingUploadResult: every field of the backend upload result plus the backward-compatible recordingId and uploadUrl.
import type { RecordingUploadResult } from '@vidtreo/recorder-react';
type RecordingUploadResult = {
recordingId: string; // same value as id
uploadUrl: string; // "" when the backend has no URL yet
id: string;
publicId: string;
filename: string;
fileSize: number;
mimeType: string;
duration: number | null;
status: string;
createdAt: string;
transferStatus: 'pending' | 'transferring' | 'transferred' | 'failed' | null;
transferError: string | null;
transferredAt: string | null;
r2DeleteAfter: string | null;
r2DeletedAt: string | null;
mediaType?: 'audio' | 'video'; // 'audio' for audio-only recordings
audioId?: string; // the audio id, set for audio-only recordings
};Upload Errors
When an upload fails with a known cause, the component shows a dialog (role="alertdialog") after recording and processing finish, and onUploadError receives an error with a code:
import VidtreoRecorder, {
getUploadErrorCode,
UPLOAD_ERROR_INSUFFICIENT_CREDITS,
} from '@vidtreo/recorder-react';
<VidtreoRecorder
apiKey="your-api-key"
onUploadError={(error) => {
if (getUploadErrorCode(error) === UPLOAD_ERROR_INSUFFICIENT_CREDITS) {
notifyAdmin();
}
}}
/>| Code | Meaning |
|------|---------|
| upload.insufficient-credits | The account has no credits left (HTTP 402). The upload stays queued and is retried every 5 minutes; onUploadError fires on each rejected attempt. The first rejection of each queued upload per page session also sets the built-in dialog and uploadErrorCode; repeated rejections only call onUploadError (no onError, no state change). |
getUploadErrorCode returns null for other errors. Network errors are retried automatically before they are reported.
Upload Resume
Uploads are queued in IndexedDB before they are sent, in every mode (desktop, mobile overlay, mobile embed, and native camera). If the page is reloaded or the connection drops, the next mounted VidtreoRecorder resumes the upload with the same idempotency key and sends only the parts that are missing. Progress shows again while it resumes, and onUploadComplete fires with the full payload when it finishes. Mount the component with the same credentials (apiKey or getAccessToken) so the upload can resume. If IndexedDB is unavailable, uploads go directly to the backend without persistence.
Minimum Recording Time
<VidtreoRecorder
apiKey="your-api-key"
minTimeRecord={10_000}
/>Hook API
For custom implementations, use the useVidtreoRecorder hook:
import { useVidtreoRecorder } from '@vidtreo/recorder-react';
function CustomRecorder() {
const { state, actions, audioLevel } = useVidtreoRecorder({
apiKey: 'your-api-key',
onUploadComplete: (result) => {
console.log('Upload complete:', result);
},
});
return (
<div>
{state.stream && (
<video srcObject={state.stream} autoPlay muted />
)}
<button onClick={() => actions.startRecording()}>
Start Recording
</button>
{state.recordingState === 'recording' && (
<button onClick={() => actions.stopRecording()}>
Stop
</button>
)}
<div>Timer: {state.timer}</div>
{state.error && <div>Error: {state.error}</div>}
</div>
);
}Hook State
{
recordingState: 'idle' | 'countdown' | 'recording';
stream: MediaStream | null;
isMuted: boolean;
isPaused: boolean;
error: string | null;
countdown: number | null;
timer: string;
uploadProgress: number | null;
uploadPhase: UploadProgressPhase | null; // 'preparing' | 'transferring' | 'finalizing' | 'completed'
uploadRetryAttempt: number | null;
uploadErrorCode: UploadErrorCode | null; // 'upload.insufficient-credits'
transcodingProgress: number | null;
devices: {
cameras: MediaDeviceInfo[];
microphones: MediaDeviceInfo[];
selectedCamera: string | null;
selectedMic: string | null;
};
}Hook Actions
startRecording(sourceType?: 'camera' | 'screen' | 'audio')- Start recordingstopRecording()- Stop recording and upload. Resolves with{ recordingId, uploadUrl, blob, mediaType }cancelCountdown()- Cancel the countdown before recording starts; does nothing once the countdown has endedpauseRecording()- Pause recordingresumeRecording()- Resume recordingtoggleMute()- Toggle mute stateswitchSource(sourceType: 'camera' | 'screen')- Switch between camera and screenreselectScreenSource()- Pick another screen or window without changing the source type (resolvesfalsewhen cancelled)changeCamera(deviceId: string)- Change camera devicechangeMic(deviceId: string)- Change microphone devicestartPreview(sourceType?: 'camera' | 'screen' | 'audio')- Start preview streamdownloadVideo()- Download the recorded video (demo mode)clearUploadError()- ClearuploadErrorCodeafter the user dismisses the upload errorsetRecordingTitle(title: string | null)- Name the uploaded file<title>.<ext>mark(marker: { id: string; label: string })- Record the current position in the recording. See Markerscleanup()- Clean up resources
Hook Return
The hook also returns audioLevel (0-100) for audio visualization.
Markers
Call actions.mark({ id, label }) while recording to note where something happens, for example when your UI moves on to the next question. The SDK stores the position in seconds of the recording (without paused time, rounded to the millisecond) and uploads the markers with that recording. The backend returns them as markers on the video or audio.
import { useState } from 'react';
import { useVidtreoRecorder } from '@vidtreo/recorder-react';
function Interview({ questions }: { questions: { id: string; text: string }[] }) {
const { state, actions } = useVidtreoRecorder({ apiKey: 'your-api-key' });
const [index, setIndex] = useState(0);
const next = () => {
const question = questions[index + 1];
actions.mark({ id: question.id, label: question.text });
setIndex(index + 1);
};
return (
<div>
<p>{questions[index].text}</p>
{state.recordingState === 'recording' && (
<button onClick={next}>Next question</button>
)}
</div>
);
}For example, actions.mark({ id: 'q2', label: 'Second question' }) when the second question appears gives the uploaded video markers: [{ id: 'q2', time: 42.318, label: 'Second question' }].
- Labels are free text. The SDK never reads or changes them.
- Each recording starts with no markers. Without
mark()nothing changes and the upload has nomarkerskey. - Markers are kept in the upload queue, so they are sent when an upload resumes after a reload.
- With
autoUpload: false,onRecordingReadyreceives them inrecording.markers. mark()throws aRecordingMarkerErrorfrom@vidtreo/recorderwhen no recording is running (a paused recording counts as running), whenidis not 1 to 64 characters orlabelis not 1 to 500 characters, when theidis already used in the recording, or after 200 markers. Itscodeis one ofrecording.marker-not-recording,recording.marker-invalid-id,recording.marker-invalid-label,recording.marker-duplicate-idorrecording.marker-limit-reached.- The stock
VidtreoRecordercomponent has no markers UI. Callmark()through its ref (see Markers with the component) or use the hook.
Markers with the component
Pass a ref to the stock VidtreoRecorder to mark from your own UI:
import { useRef } from 'react';
import VidtreoRecorder, {
type VidtreoRecorderHandle,
} from '@vidtreo/recorder-react';
function Interview() {
const recorderRef = useRef<VidtreoRecorderHandle>(null);
const showSecondQuestion = () => {
recorderRef.current?.mark({ id: 'q2', label: 'Second question' });
};
return (
<div>
<VidtreoRecorder ref={recorderRef} apiKey="your-api-key" />
<button onClick={showSecondQuestion}>Next question</button>
</div>
);
}recorderRef.current.mark()follows the same rules asactions.mark(). Before the recorder is ready or when no recording is running, it throws aRecordingMarkerErrorwith coderecording.marker-not-recording.- It marks the recording of whichever recorder the component shows: desktop, mobile embed or mobile overlay. In native camera mode (
nativeCameraormobileMode="native") it does nothing, because the device's camera app records outside the page. isRecordingMarkerError, theERROR_RECORDING_MARKER_*codes and theRecordingMarkerErrortype are exported from@vidtreo/recorder-react, so you don't need to import@vidtreo/recorder.
Environment Variables
The package supports reading the API key from environment variables:
- Vite/Client-side:
import.meta.env.VIDTREO_API_KEY - Node/SSR:
process.env.VIDTREO_API_KEY
For Vite projects, prefix with VITE_:
# .env
VITE_VIDTREO_API_KEY=your-api-keyServer-Side Rendering (SSR)
SSR support is planned for a future release. The architecture is prepared for SSR with service wrappers that can route through proxy endpoints.
Examples
Basic Usage
import VidtreoRecorder from '@vidtreo/recorder-react';
function App() {
return (
<VidtreoRecorder
apiKey="your-api-key"
/>
);
}Custom UI with Hook
import { useVidtreoRecorder } from '@vidtreo/recorder-react';
function CustomRecorder() {
const { state, actions } = useVidtreoRecorder({
apiKey: 'your-api-key',
});
return (
<div>
<video srcObject={state.stream} autoPlay muted />
<div>
{state.recordingState === 'idle' ? (
<button onClick={() => actions.startRecording()}>
Start
</button>
) : (
<button onClick={() => actions.stopRecording()}>
Stop
</button>
)}
</div>
{state.timer && <div>{state.timer}</div>}
</div>
);
}Package Structure
This package contains:
- React component (
VidtreoRecorder) - React hook (
useVidtreoRecorder) - UI sub-components
- TypeScript types
The core SDK functionality is provided by the @vidtreo/recorder package.
Internationalization (i18n)
The React component supports multiple languages and custom text overrides, making it easy to localize the recorder interface for your users.
Supported Languages
- English (
en) - Default - Spanish (
es)
Basic Usage
Using Built-in Languages
import VidtreoRecorder from '@vidtreo/recorder-react';
// English (default)
function App() {
return (
<VidtreoRecorder
apiKey="your-api-key"
/>
);
}
// Spanish
function App() {
return (
<VidtreoRecorder
apiKey="your-api-key"
lang="es"
/>
);
}Custom Text Overrides
Override any text by passing an object to the texts prop:
import VidtreoRecorder from '@vidtreo/recorder-react';
function App() {
return (
<VidtreoRecorder
apiKey="your-api-key"
lang="en"
texts={{
record: 'Start Recording',
stop: 'End Recording',
settings: 'Options'
}}
/>
);
}Complete Custom Translations (e.g., French)
import VidtreoRecorder from '@vidtreo/recorder-react';
function App() {
const frenchTranslations = {
initializingCamera: 'Initialisation de la caméra...',
grantPermissions: 'Accordez les autorisations de caméra et de microphone',
switchingDevice: 'Changement d\'appareil...',
recordingStartsIn: 'L\'enregistrement commence dans...',
switchingSource: 'Changement de source...',
rec: 'ENREG',
settings: 'Paramètres',
record: 'Enregistrer',
stop: 'Arrêter',
pause: 'Pause',
resume: 'Reprendre',
mute: 'Muet',
unmute: 'Activer le son',
switchSource: 'Changer de Source',
camera: 'Caméra',
microphone: 'Microphone',
uploading: 'Téléchargement...',
};
return (
<VidtreoRecorder
apiKey="your-api-key"
lang="fr"
texts={frenchTranslations}
/>
);
}Dynamic Language Change
import { useState } from 'react';
import VidtreoRecorder from '@vidtreo/recorder-react';
function App() {
const [language, setLanguage] = useState('en');
return (
<div>
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
</select>
<VidtreoRecorder
apiKey="your-api-key"
lang={language}
/>
</div>
);
}Available Translation Keys
| Key | English Default | Spanish Default |
|-----|----------------|-----------------|
| initializingCamera | Initializing camera... | Inicializando cámara... |
| grantPermissions | Grant camera and microphone permissions when prompted | Otorga permisos de cámara y micrófono cuando se solicite |
| browserUnsupported | This browser is not supported | Este navegador no es compatible |
| browserUnsupportedDynamic | {browser} ({version}) is not supported, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> | {browser} ({version}) no es compatible, por favor usa <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> |
| browserUnsupportedSafari | Safari (latest stable) is supported. If you experience issues, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> | Safari (última versión estable) es compatible. Si experimentas problemas, por favor usa <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> |
| browserUnsupportedFirefox | Firefox is not supported, please use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> | Firefox no es compatible, por favor use <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer">Chrome</a> |
| retryCamera | Retry Camera | Reintentar Cámara |
| cameraInUse | Your camera is being used by another application. Close other apps using the camera (Zoom, Teams, Discord) and try again. | Tu cámara está siendo usada por otra aplicación. Cierra otras apps que usen la cámara (Zoom, Teams, Discord) e intenta de nuevo. |
| cameraNotFound | No camera detected. Connect a camera or check your device settings. | No se detectó ninguna cámara. Conecta una cámara o revisa la configuración de tu dispositivo. |
| cameraPermissionDenied | Camera access was denied. Allow camera access in your browser settings and try again. | Se denegó el acceso a la cámara. Permite el acceso a la cámara en la configuración de tu navegador e intenta de nuevo. |
| cameraTimeout | The camera took too long to start. Close other apps using the camera and try again. | La cámara tardó demasiado en iniciar. Cierra otras apps que usen la cámara e intenta de nuevo. |
| failedToStartCamera | Failed to start camera | Error al iniciar la cámara |
| audioInUse | Your microphone is being used by another application. Close other apps using the microphone (Zoom, Teams, Discord) and try again. | Tu micrófono está siendo usado por otra aplicación. Cierra otras apps que usen el micrófono (Zoom, Teams, Discord) e intenta de nuevo. |
| audioNotFound | No microphone detected. Connect a microphone or check your device settings. | No se detectó ningún micrófono. Conecta un micrófono o revisa la configuración de tu dispositivo. |
| audioPermissionDenied | Microphone access was denied. Allow microphone access in your browser settings and try again. | Se denegó el acceso al micrófono. Permite el acceso al micrófono en la configuración de tu navegador e intenta de nuevo. |
| audioTimeout | The microphone took too long to start. Close other apps using the microphone and try again. | El micrófono tardó demasiado en iniciar. Cierra otras apps que usen el micrófono e intenta de nuevo. |
| failedToStartAudio | Failed to start microphone | Error al iniciar el micrófono |
| audioErrorTitle | Microphone unavailable | Micrófono no disponible |
| audioErrorMessage | We couldn't access your microphone. It may be in use by another app like Zoom, Teams, or Discord. Please close it and try again. | No pudimos acceder a tu micrófono. Puede estar en uso por otra app como Zoom, Teams o Discord. Ciérrala e inténtalo de nuevo. |
| audioErrorRetry | Try again | Intentar de nuevo |
| audioMissingTitle | Recording has no audio | La grabación no tiene audio |
| audioMissingMessage | We couldn't capture audio in this recording. Your microphone may have been disconnected or blocked by the browser. Check your microphone and record again. | No pudimos capturar audio en esta grabación. Es posible que el micrófono se haya desconectado o que el navegador lo haya bloqueado. Verifica tu micrófono y vuelve a grabar. |
| audioSilentTitle | Recording is silent | La grabación quedó en silencio |
| audioSilentMessage | We didn't detect any sound during the recording. Make sure your microphone is working and not muted by your operating system, then record again. | No detectamos sonido durante la grabación. Verifica que tu micrófono esté funcionando y no esté silenciado por el sistema, luego vuelve a grabar. |
| audioProcessingErrorTitle | Audio processing error | Error al procesar el audio |
| audioProcessingErrorMessage | We couldn't process the audio in this recording. Please record again. | No pudimos procesar el audio de esta grabación. Por favor vuelve a grabar. |
| audioMissingRetry | Record again | Grabar de nuevo |
| audioWarningNoSignalTitle | No microphone signal | Sin señal del micrófono |
| audioWarningNoSignalMessage | We're not detecting any sound. Check that your microphone is active. | No estamos detectando sonido. Verifica que tu micrófono esté activo. |
| audioWarningLowSignalTitle | Audio is very quiet | Audio muy bajo |
| audioWarningLowSignalMessage | Move closer to the microphone or turn up the input volume. | Acércate al micrófono o sube el volumen de entrada. |
| audioWarningSpeakingMutedTitle | You're speaking while muted | Estás hablando muteado |
| audioWarningSpeakingMutedMessage | Unmute your microphone if you want your voice in the recording. | Activa el micrófono si quieres que tu voz se escuche en la grabación. |
| audioWarningTrackEndedTitle | Microphone disconnected | Micrófono desconectado |
| audioWarningTrackEndedMessage | The microphone was disconnected. Reconnect it before continuing. | Se desconectó el micrófono. Reconéctalo antes de continuar. |
| audioWarningNoChunksTitle | No microphone input | Sin entrada del micrófono |
| audioWarningNoChunksMessage | We're not receiving audio from the microphone. Check browser permissions. | No estamos recibiendo audio del micrófono. Revisa los permisos del navegador. |
| audioWarningTrackMutedTitle | Microphone muted by browser | Micrófono silenciado por el navegador |
| audioWarningTrackMutedMessage | The browser muted your microphone. Check site settings. | El navegador silenció tu micrófono. Revisa la configuración del sitio. |
| audioWarningDismiss | Dismiss | Cerrar |
| cameraErrorTitle | Camera unavailable | Cámara no disponible |
| cameraErrorMessage | We couldn't access your camera. It may be in use by another app. Please close it and try again. | No pudimos acceder a tu cámara. Puede estar en uso por otra app. Ciérrala e inténtalo de nuevo. |
| browserErrorTitle | Browser not supported | Navegador no compatible |
| browserErrorMessage | Your browser doesn't support video recording. Please use the latest version of Chrome for the best experience. | Tu navegador no es compatible con la grabación de video. Por favor usa la última versión de Chrome. |
| recordingCodecUnsupportedTitle | Recording format not supported | Formato de grabación no compatible |
| recordingCodecUnsupportedMessage | This device can't use the selected video format. Close other apps, update Chrome if possible, and record again. | Este dispositivo no puede usar el formato de video seleccionado. Cierra otras apps, actualiza Chrome si es posible y vuelve a grabar. |
| recordingMediaEmptyTitle | No video was produced | No se generó ningún video |
| recordingMediaEmptyMessage | The browser didn't produce a usable video. Check your camera and record again. | El navegador no produjo un video utilizable. Verifica la cámara y vuelve a grabar. |
| recordingMediaErrorTitle | Recording could not be finished | No se pudo finalizar la grabación |
| recordingMediaErrorMessage | The browser reported an error before the video was ready to upload. Record again. | El navegador informó un error antes de preparar el video para subirlo. Vuelve a grabar. |
| recordingServerTranscodeFallbackTitle | Video processing failed | No se pudo procesar el video |
| recordingServerTranscodeFallbackMessage | We couldn't prepare this video for upload on your device. Record again. | No pudimos preparar este video para subirlo en tu dispositivo. Vuelve a grabar. |
| recordingTrackEndedTitle | Camera stopped during recording | La cámara se detuvo durante la grabación |
| recordingTrackEndedMessage | The camera feed ended before the recording finished. Check that the camera is still connected and record again. | La señal de la cámara terminó antes de finalizar la grabación. Verifica que la cámara siga conectada y vuelve a grabar. |
| recordingFinalizeTimeoutTitle | Recording took too long to finish | La grabación tardó demasiado en finalizar |
| recordingFinalizeTimeoutMessage | This device may be low on memory or processing power. Close other apps or browser tabs, then record again. | Es posible que este dispositivo tenga poca memoria o potencia de procesamiento. Cierra otras apps o pestañas del navegador y vuelve a grabar. |
| recordingStateRaceTitle | Recording did not start correctly | La grabación no inició correctamente |
| recordingStateRaceMessage | The recording could not be finalized because it did not start cleanly. Please refresh the page and record again. | No se pudo finalizar la grabación porque no inició correctamente. Actualiza la página y vuelve a grabar. |
| recordingNoFramesTitle | No video was captured | No se capturó video |
| recordingNoFramesMessage | We couldn't receive video frames from the camera. Check your camera connection and record again. | No pudimos recibir fotogramas de video de la cámara. Verifica la conexión de la cámara y vuelve a grabar. |
| recordingUnsupportedTitle | Recording not available | Grabación no disponible |
| recordingUnsupportedMessage | This device or browser can't record video reliably right now. Check your connection, update Chrome if possible, or use another device. | Este dispositivo o navegador no puede grabar video de forma estable ahora. Revisa tu conexión, actualiza Chrome si es posible o usa otro dispositivo. |
| recordingPipelineRetry | Record again | Grabar de nuevo |
| genericErrorTitle | Something went wrong | Algo salió mal |
| genericErrorMessage | An unexpected error occurred. Please try again. | Ocurrió un error inesperado. Por favor inténtalo de nuevo. |
| errorRetry | Try again | Intentar de nuevo |
| switchingDevice | Switching device... | Cambiando dispositivo... |
| recordingStartsIn | Recording starts in... | La grabación comienza en... |
| switchingSource | Switching source... | Cambiando fuente... |
| rec | REC | GRAB |
| settings | Settings | Configuración |
| record | Record | Grabar |
| stop | Stop | Detener |
| pause | Pause | Pausar |
| resume | Resume | Reanudar |
| mute | Mute | Silenciar |
| unmute | Unmute | Activar sonido |
| switchSource | Switch Source | Cambiar Fuente |
| camera | Camera | Cámara |
| microphone | Microphone | Micrófono |
| microphoneConnecting | Connecting microphone... | Conectando micrófono... |
| minimumRecordingTimeNotReached | You have not met the minimum recording time yet | Aun no cumples el tiempo minimo de grabacion |
| uploading | Uploading... | Subiendo... |
| finalizing | Finalizing… | Finalizando… |
| retryingUpload | Retrying upload... | Reintentando subida... |
| uploadInsufficientCredits | This video couldn't be uploaded. Please contact the site administrator. | No se pudo subir el video. Contacta al administrador del sitio. |
| uploadInsufficientCreditsTitle | Video couldn't be uploaded | No se pudo subir el video |
| uploadErrorClose | Close | Cerrar |
| finishing | Finishing... | Finalizando... |
| userInAnotherTab | User in another tab | Usuario en otra pestaña |
| download | Download | Descargar |
| nativeCameraSelectVideo | Select Video from Gallery | Seleccionar Video de la Galería |
| nativeCameraRecordVideo | Record with Camera | Grabar con Cámara |
| nativeCameraProcessing | Processing video... | Procesando video... |
| nativeCameraConfigLoading | Loading settings... | Cargando configuración... |
| nativeCameraConfigLoadError | Failed to load settings | No se pudo cargar la configuración |
| nativeCameraPreviewAlt | Video preview | Vista previa del video |
| errorTitle | Configuration Error | Error de Configuración |
| errorApiKeyRequired | An API Key or access token provider is required. | Se requiere una API Key o un proveedor de token de acceso. |
| errorGeneric | An error occurred while initializing the recorder | Ocurrió un error al inicializar la grabadora |
| mobileOpenCamera | Allow Access | Permitir Acceso |
| mobileOpenCameraTitle | Camera & Microphone | Cámara y Micrófono |
| mobileOpenCameraDescription | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
| mobileCloseCamera | Close | Cerrar |
| mobileHideRecordingPrompt | Hide prompt | Ocultar pregunta |
| mobileShowRecordingPrompt | Show prompt | Mostrar pregunta |
| mobilePermissionDenied | Camera Access Denied | Acceso a Cámara Denegado |
| mobilePermissionDeniedDescription | Please allow camera access in your browser settings to record video. | Por favor permite el acceso a la cámara en la configuración de tu navegador para grabar video. |
| permissionFlowTitle | Set Up Your Camera | Configurar tu Cámara |
| permissionFlowCameraTitle | Camera & Microphone | Cámara y Micrófono |
| permissionFlowMicrophoneTitle | Camera & Microphone | Cámara y Micrófono |
| permissionFlowCameraLabel | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
| permissionFlowMicrophoneLabel | We need access to your camera and microphone to start recording | Necesitamos acceso a tu cámara y micrófono para comenzar a grabar |
| permissionFlowStatusPending | Pending | Pendiente |
| permissionFlowStatusGranted | Ready | Listo |
| permissionFlowStatusDenied | Blocked | Bloqueado |
| permissionFlowStatusRequesting | Click "Allow" in the browser popup above | Haz clic en "Permitir" en el aviso del navegador |
| permissionFlowAllowCamera | Allow Access | Permitir Acceso |
| permissionFlowAllowMicrophone | Allow Access | Permitir Acceso |
| permissionFlowRetry | Try Again | Reintentar |
| permissionFlowRecoveryTitle | Allow access to continue | Permite el acceso para continuar |
| permissionFlowRecoveryInstructions | Open your browser settings and allow camera and microphone access, then try again. | Abre la configuración de tu navegador y permite el acceso a la cámara y micrófono, luego intenta de nuevo. |
| permissionFlowConnecting | Setting up... | Preparando... |
| initializingMicrophone | Initializing microphone... | Iniciando micrófono... |
| grantMicrophonePermission | Grant microphone permission when prompted | Concede el permiso de micrófono cuando se te solicite |
| retryMicrophone | Retry Microphone | Reintentar micrófono |
| audioOnlyReady | Ready to record | Listo para grabar |
| audioOnlyRecording | Recording | Grabando |
| audioOnlyPaused | Paused | En pausa |
| audioOnlyUploading | Uploading {progress}% | Subiendo {progress}% |
| audioOnlyUploaded | Upload complete | Subida completada |
| audioOnlyRecordingReady | Recording ready | Grabación lista |
| audioOnlyElapsedTime | Elapsed time | Tiempo transcurrido |
| audioOnlyNoMicrophones | No microphones available | No hay micrófonos disponibles |
| audioRecordingDisabledTitle | Audio recording unavailable | Grabación de audio no disponible |
| audioRecordingDisabled | Audio recording is not enabled for this environment | La grabación de audio no está habilitada para este entorno |
| permissionFlowMicrophoneOnlyTitle | Microphone | Micrófono |
| permissionFlowMicrophoneOnlyLabel | We need access to your microphone to start recording | Necesitamos acceso a tu micrófono para comenzar a grabar |
| permissionFlowMicrophoneOnlyRecoveryInstructions | Open your browser settings and allow microphone access, then try again. | Abre la configuración de tu navegador y permite el acceso al micrófono, luego intenta de nuevo. |
| mobileOpenMicrophoneTitle | Microphone | Micrófono |
| mobileOpenMicrophoneDescription | We need access to your microphone to start recording | Necesitamos acceso a tu micrófono para comenzar a grabar |
TranslationKeys lists every available key.
TypeScript Support
The package exports types for better type safety, including PartialTranslations, TranslationKeys, RecordingUploadResult, UploadErrorCode, UploadProgressPhase, and AccessTokenProvider. It also re-exports getUploadErrorCode, UPLOAD_ERROR_INSUFFICIENT_CREDITS, isCredentialConflictError, CONFIG_ERROR_CREDENTIAL_CONFLICT, isRecordingMarkerError, the ERROR_RECORDING_MARKER_* codes and the RecordingMarkerError type from @vidtreo/recorder. VidtreoRecorderHandle types the component's ref.
import VidtreoRecorder from '@vidtreo/recorder-react';
import type { PartialTranslations } from '@vidtreo/recorder-react';
function App() {
const customTexts: PartialTranslations = {
record: 'Start Capture',
stop: 'End Capture',
};
return (
<VidtreoRecorder
apiKey="your-api-key"
texts={customTexts}
/>
);
}Multilingual Application with Context
import { createContext, useContext, useState } from 'react';
import VidtreoRecorder from '@vidtreo/recorder-react';
const LanguageContext = createContext({
language: 'en',
setLanguage: (lang: string) => {}
});
function LanguageProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguage] = useState('en');
return (
<LanguageContext.Provider value={{ language, setLanguage }}>
{children}
</LanguageContext.Provider>
);
}
function RecorderComponent() {
const { language } = useContext(LanguageContext);
return (
<VidtreoRecorder
apiKey="your-api-key"
lang={language}
/>
);
}
function LanguageSelector() {
const { language, setLanguage } = useContext(LanguageContext);
return (
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
</select>
);
}
function App() {
return (
<LanguageProvider>
<LanguageSelector />
<RecorderComponent />
</LanguageProvider>
);
}Partial Override Example
Use a base language but override specific texts:
import VidtreoRecorder from '@vidtreo/recorder-react';
function App() {
return (
<VidtreoRecorder
apiKey="your-api-key"
lang="en"
texts={{
record: '🔴 Start Capture',
stop: '⏹️ End Capture',
settings: 'Options'
}}
/>
);
}Loading Translations from API
import { useState, useEffect } from 'react';
import VidtreoRecorder from '@vidtreo/recorder-react';
import type { PartialTranslations } from '@vidtreo/recorder-react';
function App() {
const [language, setLanguage] = useState('en');
const [translations, setTranslations] = useState<PartialTranslations>({});
useEffect(() => {
// Load translations from your API
fetch(`/api/translations/${language}`)
.then(res => res.json())
.then(data => setTranslations(data));
}, [language]);
return (
<VidtreoRecorder
apiKey="your-api-key"
lang={language}
texts={translations}
/>
);
}License
MIT
