Skip to main content

Overview

The Video plugin provides a complete solution for adding videos to your content. It supports file uploads, embedding from video providers (YouTube, Vimeo, Dailymotion, Loom, Wistia), video resizing, and various playback options.

Installation

npm install @yoopta/video

Basic Usage

import { Video } from '@yoopta/video';

const plugins = [
  Video,
  // ... other plugins
];

<YooptaEditor editor={editor} plugins={plugins} />;
Required ConfigurationYou must configure the upload and delete options. Without these options, you’ll see an error when trying to use the Video plugin.
// Required when using themes
const plugins = [
  Video.extend({
    options: {
      upload: async (file) => {
        // Your upload logic here
        return { id: '...', src: '...' };
      },
      delete: async (src) => {
        // Your delete logic here
      },
    },
  }),
];
See the Configuration section below for detailed examples.

Features

  • File Upload: Upload videos from device
  • Provider Embedding: Embed videos from YouTube, Vimeo, Dailymotion, Loom, and Wistia
  • URL Insertion: Insert videos via URL (automatically detects provider)
  • Custom Upload Functions: Direct uploads to Cloudinary, S3, Firebase, Mux, etc.
  • Video Resizing: Resize videos while maintaining aspect ratio
  • Size Limits: Set maximum width and height
  • Object Fit: Control how videos fit their container
  • Playback Settings: Configure controls, loop, muted, and autoplay
  • Poster Images: Set custom poster/thumbnail images
  • Video Deletion: Optional deletion handling with custom functions
  • Responsive: Automatically adapts to screen size

Configuration

The Video plugin supports two approaches for upload and delete operations:
  1. Endpoint-based: Configure an API endpoint and the plugin handles the request
  2. Custom function: Provide your own async function for complete control (useful for direct uploads to Cloudinary, S3, Firebase, Mux, etc.)

Endpoint-based Upload (Backend API)

import { Video } from '@yoopta/video';

const plugins = [
  Video.extend({
    options: {
      upload: {
        endpoint: '/api/upload-video',
        method: 'POST',
        headers: {
          Authorization: `Bearer ${token}`,
        },
        onSuccess: (result) => {
          console.log('Upload successful:', result);
        },
        onError: (error) => {
          console.error('Upload failed:', error);
        },
      },
      delete: {
        endpoint: '/api/delete-video',
        method: 'DELETE',
      },
      maxSizes: {
        maxWidth: 800,
        maxHeight: 600,
      },
      defaultSettings: {
        controls: true,
        loop: false,
        muted: false,
        autoPlay: false,
      },
    },
  }),
];

Custom Upload Function (Direct to Cloud)

For direct uploads to third-party services like Cloudinary, AWS S3, Firebase Storage, or Mux, you can provide a custom async function:
import { Video } from '@yoopta/video';

const plugins = [
  Video.extend({
    options: {
      // Custom upload function
      upload: async (file, onProgress) => {
        const formData = new FormData();
        formData.append('file', file);
        formData.append('upload_preset', 'your_preset');

        const response = await fetch(
          'https://api.cloudinary.com/v1_1/your_cloud/video/upload',
          { method: 'POST', body: formData }
        );
        const data = await response.json();

        // Return VideoUploadResponse
        return {
          id: data.public_id,
          src: data.secure_url,
          sizes: {
            width: data.width,
            height: data.height,
          },
          poster: data.thumbnail_url,
        };
      },
      // Custom delete function
      delete: async (src) => {
        // Extract public_id and delete from your service
        const publicId = extractPublicId(src);
        await fetch(`/api/cloudinary-delete/${publicId}`, { method: 'DELETE' });
      },
      maxSizes: {
        maxWidth: 800,
        maxHeight: 600,
      },
    },
  }),
];

Provider URL Embedding

The Video plugin automatically detects and embeds videos from supported providers when you paste a URL:
import { Video } from '@yoopta/video';

const plugins = [
  Video.extend({
    options: {
      upload: async (file) => {
        // Your upload logic
        return { id: '...', src: '...' };
      },
      delete: async (src) => {
        // Your delete logic
      },
      // Optional: Restrict which providers are allowed
      allowedProviders: ['youtube', 'vimeo'], // Only allow YouTube and Vimeo
    },
  }),
];
Supported Providers:
  • YouTube: youtube.com, youtu.be
  • Vimeo: vimeo.com
  • Dailymotion: dailymotion.com, dai.ly
  • Loom: loom.com
  • Wistia: wistia.com, wistia.net
When a user pastes a URL from any of these providers, the plugin automatically:
  1. Detects the provider
  2. Extracts the video ID
  3. Generates the embed URL
  4. Optionally fetches the thumbnail

Options

upload

Can be either an object (endpoint-based) or a function (custom upload):
upload
object

delete

Can be either an object (endpoint-based) or a function (custom delete):
delete
object
uploadPoster
object | function
Optional. Upload function for poster/thumbnail images. Can be endpoint-based or custom function, similar to upload.
maxSizes
object
defaultSettings
object
allowedProviders
array
Optional. Array of allowed video provider types. If not set, all providers are allowed.Supported values: 'youtube' | 'vimeo' | 'dailymotion' | 'loom' | 'wistia'
accept
string
default:"video/*"
Accepted video file types (e.g., “video/mp4,video/webm,video/ogg”)
maxFileSize
number
Maximum file size in bytes

Element Props

src
string | null
Video source URL (for direct video files) or embed URL (for provider videos)
provider
object | null
Provider information for embedded videos:
{
  type: 'youtube' | 'vimeo' | 'dailymotion' | 'loom' | 'wistia' | 'custom' | null;
  id: string;
  url?: string;
}
settings
object
Video playback settings:
{
  controls?: boolean;
  loop?: boolean;
  muted?: boolean;
  autoPlay?: boolean;
}
sizes
object
Video dimensions: { width: number | string, height: number | string }
fit
'contain' | 'cover' | 'fill' | null
How the video should fit its container
poster
string | null
URL of the poster/thumbnail image to display before video loads
srcSet
string | null
Responsive video source set
bgColor
string | null
Background color for video container

Commands

import { VideoCommands } from '@yoopta/video';

// Insert a video
VideoCommands.insertVideo(editor, {
  props: {
    src: 'https://example.com/video.mp4',
    sizes: { width: 650, height: 400 },
  },
});

// Update video properties
VideoCommands.updateVideo(editor, blockId, {
  src: 'https://example.com/new-video.mp4',
  settings: {
    controls: true,
    loop: false,
  },
});

// Delete video
VideoCommands.deleteVideo(editor, blockId);

Provider Utilities

The Video plugin exports utility functions for working with video providers:
import {
  parseVideoUrl,
  buildVideoProvider,
  getEmbedUrl,
  isValidVideoUrl,
  isProviderUrl,
  getSupportedProviders,
  isProviderSupported,
} from '@yoopta/video';

// Parse a video URL and extract provider information
const parsed = parseVideoUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
// {
//   provider: 'youtube',
//   id: 'dQw4w9WgXcQ',
//   originalUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
//   embedUrl: 'https://www.youtube.com/embed/dQw4w9WgXcQ',
//   thumbnailUrl: 'https://img.youtube.com/vi/dQw4w9WgXcQ/hqdefault.jpg',
//   isValid: true
// }

// Check if a URL is a valid video URL
const isValid = isValidVideoUrl('https://www.youtube.com/watch?v=dQw4w9WgXcQ');

// Check if a URL is from a specific provider
const isYouTube = isProviderUrl('https://www.youtube.com/watch?v=...', 'youtube');

// Get embed URL for a provider
const embedUrl = getEmbedUrl('youtube', 'dQw4w9WgXcQ');
// 'https://www.youtube.com/embed/dQw4w9WgXcQ'

// Get all supported providers
const providers = getSupportedProviders();
// ['youtube', 'vimeo', 'dailymotion', 'loom', 'wistia']

// Check if a provider is supported
const isSupported = isProviderSupported('youtube');

Upload Response Format

Your upload endpoint should return:
{
  id: string;
  src: string;
  width?: number;
  height?: number;
  poster?: string;
  duration?: number;
  size?: number;
  format?: string;
  provider?: {
    type: string;
    id: string;
    url?: string;
  };
}

Custom Rendering

import { Video } from '@yoopta/video';

const CustomVideo = Video.extend({
  elements: {
    video: {
      render: (props) => {
        const { src, provider, sizes, settings, poster } = props.element.props;

        return (
          <div {...props.attributes} contentEditable={false}>
            {provider && provider.type ? (
              <iframe
                src={getEmbedUrl(provider.type, provider.id)}
                width={sizes?.width}
                height={sizes?.height}
                frameBorder="0"
                allowFullScreen
              />
            ) : (
              <video
                src={src}
                controls={settings?.controls}
                loop={settings?.loop}
                muted={settings?.muted}
                autoPlay={settings?.autoPlay}
                poster={poster || undefined}
                width={sizes?.width}
                height={sizes?.height}
              />
            )}
            {props.children}
          </div>
        );
      },
    },
  },
});

Parsers

HTML Deserialization

The plugin automatically deserializes <video> tags:
<video src="video.mp4" width="650" height="400" controls loop muted autoplay />

HTML Serialization

<div style="display: flex; justify-content: center;">
  <video src="video.mp4" width="650" height="400" controls="true" loop="true" muted="true" autoplay="true" />
</div>

Markdown Serialization

![Video](https://example.com/video.mp4)

Use Cases

Blog Posts

Embed tutorial videos and demonstrations

Documentation

Video guides and walkthroughs

Portfolios

Showcase project videos and reels

Product Demos

Feature product videos and tutorials

Educational Content

Course videos and lectures

Social Media

Embed YouTube and Vimeo content

Best Practices

Compress videos before uploading to reduce file size and improve load times
Set a poster image to improve user experience while video loads
Configure appropriate maxWidth and maxHeight for your design
Browser autoplay policies may require videos to be muted for autoplay to work
Handle upload errors gracefully with user feedback
For large videos, consider using provider embedding (YouTube, Vimeo) instead of direct uploads

Hooks

useVideoUpload

Supports both endpoint-based and custom function approaches:
import { useVideoUpload } from '@yoopta/video';

const { upload, loading, progress, error } = useVideoUpload({
  endpoint: '/api/upload-video',
  onSuccess: (result) => {
    console.log('Uploaded:', result.url);
  },
});

// Upload a file
const handleUpload = async (file: File) => {
  const result = await upload(file);
};

useVideoDelete

Supports both endpoint-based and custom function approaches:
import { useVideoDelete } from '@yoopta/video';

const { deleteVideo, loading, error } = useVideoDelete({
  endpoint: '/api/delete-video',
});

useVideoPreview

Generate a preview URL for a video file before uploading:
import { useVideoPreview } from '@yoopta/video';

const { preview, generatePreview, clearPreview } = useVideoPreview();

const handleFileSelect = (file: File) => {
  const previewData = generatePreview(file);
  // previewData.url contains the object URL
  // Use it to show a preview before upload
};

useVideoPosterUpload

Upload poster/thumbnail images separately:
import { useVideoPosterUpload } from '@yoopta/video';

const { uploadPoster, loading, error } = useVideoPosterUpload({
  endpoint: '/api/upload-poster',
});

const handlePosterUpload = async (file: File) => {
  const posterUrl = await uploadPoster(file);
  // Use posterUrl to set the poster prop
};

Troubleshooting

This error occurs when you’re using a theme but haven’t configured the upload option.Solution: Add an upload configuration to your Video plugin:
Video.extend({
  options: {
    upload: async (file) => {
      // Your upload logic
      return { id: '...', src: '...' };
    },
  },
})
This error occurs when you’re using a theme but haven’t configured the delete option.Solution: Add a delete configuration to your Video plugin:
Video.extend({
  options: {
    delete: async (src) => {
      // Your delete logic
    },
  },
})
This error occurs when using endpoint-based configuration without providing the endpoint URL.Solution: Make sure to include the endpoint property:
Video.extend({
  options: {
    upload: {
      endpoint: '/api/upload-video', // Required!
    },
    delete: {
      endpoint: '/api/delete-video', // Required!
    },
  },
})
If a provider URL is not being detected, check:
  1. The URL format matches supported patterns
  2. The provider is in the allowedProviders list (if configured)
  3. The URL is from a supported provider (YouTube, Vimeo, Dailymotion, Loom, Wistia)
Solution: Use the parseVideoUrl utility to debug:
import { parseVideoUrl } from '@yoopta/video';
const parsed = parseVideoUrl('your-url-here');
console.log(parsed); // Check if isValid is true
Many browsers block autoplay unless the video is muted. Make sure to set muted: true when using autoPlay: true.Solution:
Video.extend({
  options: {
    defaultSettings: {
      autoPlay: true,
      muted: true, // Required for autoplay in most browsers
    },
  },
})