Plugins
Video Compressor
Compress videos in the browser using FFmpeg before upload.
Video Compressor
The PluginVideoCompressor uses FFmpeg.wasm to compress videos directly in the browser before upload.
Video compression requires additional dependencies and runs entirely in the browser. It can be CPU-intensive and may take significant time for large videos.
Installation
First, install the required FFmpeg packages:
Terminal
pnpm add @ffmpeg/ffmpeg @ffmpeg/util
Usage
import { PluginVideoCompressor } from "nuxt-upload-kit"
const uploader = useUploadKit({
plugins: [
PluginVideoCompressor({
maxWidth: 1280,
maxHeight: 720,
videoBitrate: "1M",
audioBitrate: "128k",
}),
],
})
Options
| Option | Type | Default | Description |
|---|---|---|---|
maxWidth | number | 1920 | Maximum video width |
maxHeight | number | 1080 | Maximum video height |
videoBitrate | string | '2M' | Video bitrate (e.g., '1M', '2M', '500k') |
audioBitrate | string | '128k' | Audio bitrate |
fps | number | 30 | Target frame rate |
codec | string | 'libx264' | Video codec |
preset | string | 'medium' | Encoding preset (ultrafast to veryslow) |
crf | number | 23 | Constant Rate Factor (0-51, lower = better quality) |
How It Works
- Load FFmpeg - Downloads FFmpeg.wasm (~30MB) on first use
- Write file - Copies video to FFmpeg's virtual filesystem
- Transcode - Runs FFmpeg with specified options
- Read output - Retrieves compressed video
- Compare - Only uses compressed version if smaller
Using the FFmpeg Composable
For more control, use the useFFMpeg composable directly:
const { load, convert, unload, status, progress, onConvertSuccess } = useFFMpeg({
inputUrl: videoUrl,
convertOptions: ["-preset", "medium", "-crf", "23"],
})
// Load FFmpeg (downloads WASM core on first call)
await load()
// Convert the video
const compressedFile = await convert([])
Encoding Presets
FFmpeg presets trade speed for compression:
| Preset | Speed | Compression | Use Case |
|---|---|---|---|
ultrafast | Fastest | Worst | Testing |
veryfast | Very fast | Poor | Quick preview |
fast | Fast | Below average | Development |
medium | Balanced | Good | Default |
slow | Slow | Better | Production |
veryslow | Slowest | Best | Maximum compression |
PluginVideoCompressor({
preset: "fast", // Faster encoding, larger files
})
CRF (Quality)
Constant Rate Factor controls quality vs size:
| CRF | Quality | File Size |
|---|---|---|
| 18 | Visually lossless | Largest |
| 23 | Default | Balanced |
| 28 | Good | Smaller |
| 35 | Acceptable | Much smaller |
PluginVideoCompressor({
crf: 28, // Smaller files, slightly lower quality
})
Resolution Scaling
Videos are scaled down if they exceed max dimensions:
PluginVideoCompressor({
maxWidth: 1280, // Scale to 720p max
maxHeight: 720,
})
Aspect ratio is always preserved.
Browser Compatibility
FFmpeg.wasm requires:
- SharedArrayBuffer - Requires specific HTTP headers
- WebAssembly - Supported in all modern browsers
Required HTTP Headers
Your server must return these headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
In Nuxt, add to your nuxt.config.ts:
nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
"/**": {
headers: {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin",
},
},
},
})
Progress Tracking
Video compression can take time. Track progress:
uploader.on("video-compressor:progress", ({ file, progress }) => {
console.log(`Compressing ${file.name}: ${progress}%`)
})
uploader.on("video-compressor:complete", ({ file, originalSize, compressedSize }) => {
const saved = (((originalSize - compressedSize) / originalSize) * 100).toFixed(1)
console.log(`Compressed ${file.name}: saved ${saved}%`)
})
Example: Full Video Upload Setup
<script setup>
import { PluginVideoCompressor } from 'nuxt-upload-kit'
const compressionProgress = ref<Record<string, number>>({})
const uploader = useUploadKit({
allowedFileTypes: ['video/*'],
maxFileSize: 500 * 1024 * 1024, // 500MB
plugins: [
PluginVideoCompressor({
maxWidth: 1920,
maxHeight: 1080,
videoBitrate: '2M',
preset: 'medium',
crf: 23
})
]
})
uploader.on('video-compressor:progress', ({ file, progress }) => {
compressionProgress.value[file.id] = progress
})
</script>
<template>
<div v-for="file in uploader.files" :key="file.id">
<span>{{ file.name }}</span>
<!-- Show compression progress -->
<div v-if="compressionProgress[file.id] !== undefined">Compressing: {{ compressionProgress[file.id] }}%</div>
<!-- Show upload progress -->
<div v-else-if="file.status === 'uploading'">Uploading: {{ file.progress.percentage }}%</div>
</div>
</template>
Performance Tips
- Use appropriate presets -
fastorveryfastfor development - Limit resolution - 720p is often sufficient for web
- Consider server-side - For large videos, server-side transcoding may be better
- Show progress - Long operations need user feedback
- Handle errors - FFmpeg can fail on unsupported codecs

