@vidtreo/recorder-vue
v1.6.7
Published
Vue 3 components and composables for @vidtreo/recorder - video recording SDK
Readme
@vidtreo/recorder-vue
Vue 3 components and composables for the Vidtreo video recording SDK. Drop the recorder into any Vue app with a single tag, or build a fully custom UI on top of the same battle-tested composables that power @vidtreo/recorder-react — same recording pipeline, same upload queue, same i18n, native Composition API ergonomics.
What you get out of the box
- 🎥 Camera + screen capture with one-click source switching, mute, pause/resume, and device selection.
- ⏱️ Countdown, timer, audio meter, transcoding progress, upload progress — all wired and styled.
- 🔐 Permission flow with a polished overlay that walks the user through allowing camera + mic, plus a recovery guide when they were denied.
- 📱 Mobile-aware — overlay modal, embedded mode, or the device's native camera (file input), selectable via the
mobileModeprop. - 🌐 i18n out of the box (English + Spanish) and full per-string overrides for any language you want.
- 🧪 SSR-safe — every browser API is gated to client-side mount. Drop it in Nuxt with
@vidtreo/recorder-nuxtand forget about hydration mismatches. - 🛠️ Composable API for building your own UI — same shape as
@vidtreo/recorder-react, so React-to-Vue migrations are 1:1.
Installation
npm install @vidtreo/recorder-vue @vidtreo/recorderPeer dependencies:
@vidtreo/recorder >=1.6.0vue >=3.0.0
Don't forget the stylesheet — either import it once in your entry, or rely on the built-in CSS injection (added to <head> automatically when the package loads).
// Optional explicit import — useful if you bundle styles separately
import "@vidtreo/recorder-vue/styles.css";Quick start
Drop-in component (2 lines)
<script setup lang="ts">
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
</script>
<template>
<VidtreoRecorder :recorder-props="{ apiKey: 'your-api-key' }" />
</template>Using environment variables
For Vite-based apps, expose the key via import.meta.env:
# .env
VITE_VIDTREO_API_KEY=your-api-key<script setup lang="ts">
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
const apiKey = import.meta.env.VITE_VIDTREO_API_KEY as string;
</script>
<template>
<VidtreoRecorder :recorder-props="{ apiKey }" />
</template>Component API
The component accepts a single recorder-props object so the discriminated union (demo: true vs apiKey) stays type-safe at the call site. Events use the standard Vue @event-name listener syntax.
Props (recorder-props)
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| apiKey | string | Yes* | – | API key for authentication. *Optional when demo: true. |
| demo | boolean | No | false | Demo mode. Skips upload and lets you download the local recording. |
| backendUrl | string | No | https://core.vidtreo.com | Backend API URL. |
| countdownDuration | number | No | – | Pre-recording countdown (milliseconds). |
| minTimeRecord | number | No | – | Minimum recording time in ms before Stop is allowed (e.g., 10_000). |
| maxRecordingTime | number | No | – | Maximum recording time in milliseconds. |
| userMetadata | Record<string, unknown> | No | – | Custom metadata attached to uploads. |
| enableSourceSwitching | boolean | No | true | Toggle between camera and screen. |
| enableMute | boolean | No | true | Show the mute/unmute button. |
| enablePause | boolean | No | true | Show pause/resume buttons. |
| enableDeviceChange | boolean | No | true | Show the settings panel with cam/mic selectors. |
| enableTabVisibilityOverlay | boolean | No | false | Show overlay when the user switches tabs (camera recordings only). |
| tabVisibilityOverlayText | string | No | localized default | Custom message for the tab-visibility overlay. |
| mobileMode | "embed" \| "overlay" \| "native" | No | "overlay" | Mobile experience. "embed" = same as desktop, "overlay" = fullscreen modal, "native" = device's native camera (file input). |
| nativeCamera | boolean | No | – | Deprecated — use mobileMode: "native" instead. |
| maxFileSize | number | No | – | Maximum file size in MB (native camera mode). |
| lang | string | No | "en" | Language code ("en" or "es"). |
| texts | PartialTranslations | No | – | Per-key text overrides. |
Events
| Event | Payload | Fired when |
|-------|---------|------------|
| recording-start | – | Recording begins (after the countdown finishes). |
| recording-stop | – | Recording ends and processing starts. |
| upload-progress | progress: number (0..1) | Each chunk is uploaded. |
| upload-complete | { recordingId: string; uploadUrl: string } | Upload finishes. |
| upload-error | error: Error | Upload fails. |
| transcoding-progress | progress: number (0..1) | Native-camera transcoding progress. |
| error | error: Error | Any unhandled error. |
Example with listeners
<script setup lang="ts">
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
import type { VidtreoRecorderProps } from "@vidtreo/recorder-vue";
const recorderProps: VidtreoRecorderProps = {
apiKey: "your-api-key",
countdownDuration: 3,
minTimeRecord: 10_000,
lang: "en",
};
function onComplete(result: { recordingId: string; uploadUrl: string }) {
console.log("Recording uploaded:", result.recordingId);
console.log("View at:", result.uploadUrl);
}
function onProgress(progress: number) {
console.log(`Upload: ${Math.round(progress * 100)}%`);
}
function onError(error: Error) {
console.error("Recorder error:", error.message);
}
</script>
<template>
<VidtreoRecorder
:recorder-props="recorderProps"
@upload-complete="onComplete"
@upload-progress="onProgress"
@error="onError"
/>
</template>Composable API
When the default component doesn't fit your UI, drop down to the useVidtreoRecorder composable and assemble your own.
<script setup lang="ts">
import { useVidtreoRecorder } from "@vidtreo/recorder-vue";
const { state, actions, audioLevel, controller } = useVidtreoRecorder({
apiKey: "your-api-key",
onUploadComplete: (result) => console.log("Upload complete:", result),
});
</script>
<template>
<div>
<video v-if="state.stream" :srcObject="state.stream" autoplay muted playsinline />
<button v-if="state.recordingState === 'idle'" @click="actions.startRecording()">
Start
</button>
<button v-else @click="actions.stopRecording()">
Stop
</button>
<div>Timer: {{ state.timer }}</div>
<div>Audio level: {{ audioLevel }}</div>
<div v-if="state.error" class="error">{{ state.error }}</div>
</div>
</template>What the composable returns
{
state: Reactive<VidtreoRecorderState>; // mutate freely, fully reactive
actions: VidtreoRecorderActions; // plain methods
audioLevel: Ref<number>; // 0..1 — perfect for waveform UIs
controller: ShallowRef<RecorderController>; // raw core SDK if you need it
}state
{
recordingState: "idle" | "countdown" | "recording";
stream: MediaStream | null;
isMuted: boolean;
isPaused: boolean;
isAudioReady: boolean;
hasAudioFailed: boolean;
error: string | null;
errorCode: string | null;
countdown: number | null;
timer: string; // "00:12" format
uploadProgress: number | null; // 0..1 or null
transitionMessage: string | null;
isInitialized: boolean;
recordedBlob: Blob | null; // populated in demo mode
audioWarning: AudioWarning | null;
audioRecovered: boolean;
devices: {
cameras: MediaDeviceInfo[];
microphones: MediaDeviceInfo[];
selectedCamera: string | null;
selectedMic: string | null;
};
}actions
| Action | Signature | What it does |
|--------|-----------|--------------|
| startRecording | (sourceType?: "camera" \| "screen") => Promise<void> | Begin recording (after countdown if configured). |
| stopRecording | () => Promise<RecordingStopResult> | Stop and trigger upload. |
| pauseRecording | () => void | Pause an active recording. |
| resumeRecording | () => void | Resume from pause. |
| toggleMute | () => void | Toggle the audio mute state. |
| switchSource | (sourceType: "camera" \| "screen") => Promise<void> | Swap between camera and screen during preview/recording. |
| changeCamera | (deviceId: string) => Promise<void> | Switch to a different camera. |
| changeMic | (deviceId: string) => Promise<void> | Switch to a different microphone. |
| startPreview | (sourceType?: "camera" \| "screen") => Promise<void> | Open the stream without recording. |
| stopPreview | () => void | Tear the preview stream down. |
| downloadVideo | () => void | Download the last recordedBlob (demo mode). |
| cleanup | () => void | Release all resources. Useful before unmounting. |
Specialized composables
| Composable | Purpose |
|------------|---------|
| useMobileWebRecorder(config) | Wraps useVidtreoRecorder and adds isModalOpen, openModal, closeModal, canCloseModal for the mobile overlay experience. |
| useNativeCamera(config) | File-input + transcoding flow used when mobileMode: "native". Returns { state, actions } with handleFileSelect, processAndUpload, downloadVideo, reset. |
| usePermissionFlow(options) | Drives the camera/mic permission overlay. Returns { isActive, isComplete, currentStep, steps, recoveryData, requestPermission, retryPermission } — useful if you want to build a fully custom permission UI. |
| useRecorderTranslations(translations) | Splits a TranslationKeys object into preview, buttons, and settings subsets as ComputedRefs. Accepts MaybeRefOrGetter<TranslationKeys> so it stays reactive when the language changes. |
Internationalization
Built-in: English (en) and Spanish (es). Use the lang prop to switch, and texts to override individual strings or to ship a brand-new language entirely.
Switch language
<template>
<VidtreoRecorder :recorder-props="{ apiKey, lang: 'es' }" />
</template>Override individual strings
<script setup lang="ts">
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
const recorderProps = {
apiKey: "your-api-key",
lang: "en",
texts: {
record: "🔴 Start Capture",
stop: "⏹️ End Capture",
settings: "Options",
},
};
</script>
<template>
<VidtreoRecorder :recorder-props="recorderProps" />
</template>Ship a completely custom language (e.g., French)
<script setup lang="ts">
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
import type { PartialTranslations } from "@vidtreo/recorder-vue";
const french: PartialTranslations = {
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...",
};
</script>
<template>
<VidtreoRecorder
:recorder-props="{ apiKey: 'your-api-key', lang: 'fr', texts: french }"
/>
</template>Dynamic language switching
The component is fully reactive — change lang and the UI re-localizes without a remount:
<script setup lang="ts">
import { ref } from "vue";
import { VidtreoRecorder } from "@vidtreo/recorder-vue";
const language = ref<"en" | "es">("en");
</script>
<template>
<select v-model="language">
<option value="en">English</option>
<option value="es">Español</option>
</select>
<VidtreoRecorder :recorder-props="{ apiKey: 'your-api-key', lang: language }" />
</template>Translation keys
| Key | English | Spanish |
|-----|---------|---------|
| 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 |
| 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 |
| minimumRecordingTimeNotReached | You have not met the minimum recording time yet | Aun no cumples el tiempo minimo de grabacion |
| 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 |
| uploading | Uploading... | Subiendo... |
The full key list lives in src/i18n/translations.ts — there's a key for every visible string, including the permission flow recovery guide.
Mobile experience
mobileMode controls how the recorder behaves on mobile devices:
| Value | Behavior |
|-------|----------|
| "overlay" (default) | A landing screen with "Allow Access". On tap, a fullscreen modal opens (via <Teleport>) with the same UI as desktop. |
| "embed" | The recorder embeds inline, identical to desktop. |
| "native" | Skips the SDK UI entirely and opens the device's native camera via a file input. Useful when WebCodecs isn't reliable on the user's browser. |
The <VidtreoRecorder> component probes browser capabilities on mobile and falls back gracefully when WebCodecs isn't available.
SSR (Nuxt, Astro-Vue, etc.)
Every browser API is gated to onMounted/if (typeof window !== "undefined"). If you're on Nuxt 3, the cleanest path is the @vidtreo/recorder-nuxt module — it registers the component as client-only and injects the CSS for you.
For other SSR setups, wrap the component in <ClientOnly> (Nuxt) or guard it behind a mount check in your framework's equivalent.
TypeScript
Every public surface is typed. Import types directly:
import type {
VidtreoRecorderProps,
VidtreoRecorderState,
VidtreoRecorderActions,
UseVidtreoRecorderConfig,
UseVidtreoRecorderReturn,
PartialTranslations,
TranslationKeys,
MobileMode,
AudioWarning,
RecordingStopResult,
} from "@vidtreo/recorder-vue";Package structure
| What | Where |
|------|-------|
| Top-level component | src/components/VidtreoRecorder.vue |
| Desktop / embed UI | src/components/StandardRecorder.vue |
| Mobile overlay | src/components/MobileWebRecorderUI.vue |
| Native camera | src/components/NativeCameraUI.vue |
| Composables | src/composables/ |
| UI primitives | src/components/ui/ |
| i18n data | src/i18n/translations.ts |
| Framework-agnostic helpers | src/utils/, src/services/ |
The recording engine, transcoding worker, and upload pipeline live in @vidtreo/recorder.
License
MIT
