Media

Loading...
Files
components/demo.tsx
'use client';

import React from 'react';

import { Plate } from '@udecode/plate-common/react';

import { editorPlugins } from '@/components/editor/plugins/editor-plugins';
import { useCreateEditor } from '@/components/editor/use-create-editor';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';

import { DEMO_VALUES } from './values/demo-values';

export default function Demo({ id }: { id: string }) {
  const editor = useCreateEditor({
    plugins: [...editorPlugins],
    value: DEMO_VALUES[id],
  });

  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}

Features

Media Features

  • Editable captions
  • Resizable elements

Media Support

  • File types:
    • Image
    • Video
    • Audio
    • Others (PDF, Word, etc.)
  • Video providers:
    • Local video files
    • YouTube, Vimeo, Dailymotion, Youku, Coub
  • Embed providers:
    • Tweets

Upload

  • Multiple upload methods:
    • Toolbar button with file picker
    • Drag and drop from file system
    • Paste from clipboard (images)
    • URL embedding for external media
  • Upload experience:
    • Real-time progress tracking
    • Preview during upload
    • Automatically converts the placeholder to the appropriate media element (image, video, audio, file) once the upload or embed is submitted
    • Error handling
    • File size validation
    • Type validation

Installation

npm install @udecode/plate-media

Usage

import {
  AudioPlugin,
  FilePlugin,
  ImagePlugin,
  MediaEmbedPlugin,
  PlaceholderPlugin,
  VideoPlugin,
} from '@udecode/plate-media/react';
import { SelectOnBackspacePlugin } from '@udecode/plate-select';
const plugins = [
  // ...otherPlugins,
  ImagePlugin,
  VideoPlugin,
  AudioPlugin,
  FilePlugin,
  MediaEmbedPlugin,
  SelectOnBackspacePlugin.configure({
    options: {
      query: {
        allow: [ImagePlugin.key, VideoPlugin.key, AudioPlugin.key, FilePlugin.key, MediaEmbedPlugin.key],
      },
    },
  }),
  PlaceholderPlugin.configure({
    options: { disableEmptyPlaceholder: true },
    render: { afterEditable: MediaUploadToast },
  }),
];
const components = {
  // ...otherComponents,
  [ImagePlugin.key]: ImageElement,
  [VideoPlugin.key]: VideoElement,
  [AudioPlugin.key]: AudioElement,
  [FilePlugin.key]: FileElement,
  [MediaEmbedPlugin.key]: MediaEmbedElement,
  [PlaceholderPlugin.key]: MediaPlaceholderElement,
};

Caption

To enable media captions, use the Caption Plugin.

Upload

There are two ways to implement file uploads in your editor:

  1. Using our UploadThing implementation
  2. Creating a custom implementation with your preferred upload solution

UploadThing

  1. Add MediaPlaceholderElement component

  2. Add API routes for UploadThing:

npx shadcx@latest add plate/api-uploadthing
  1. Get your secret key from UploadThing for free
  2. Add your UploadThing secret key to .env:
.env
UPLOADTHING_TOKEN=xxx

Custom Implementation

For custom implementations, you'll need to create an upload hook that matches our interface. This can work with any upload backend (AWS S3, UploadThing, Cloudinary, Firebase Storage, etc.).

The upload hook should implement this interface:

interface UseUploadFileProps {
  onUploadComplete?: (file: UploadedFile) => void;
  onUploadError?: (error: unknown) => void;
  headers?: Record<string, string>;
  onUploadBegin?: (fileName: string) => void;
  onUploadProgress?: (progress: { progress: number }) => void;
  skipPolling?: boolean;
}
 
interface UploadedFile {
  key: string;    // Unique identifier
  url: string;    // Public URL of the uploaded file
  name: string;   // Original filename
  size: number;   // File size in bytes
  type: string;   // MIME type
}

Example implementation with S3 presigned URLs:

export function useUploadFile({ 
  onUploadComplete, 
  onUploadError, 
  onUploadProgress 
}: UseUploadFileProps = {}) {
  const [uploadedFile, setUploadedFile] = useState<UploadedFile>();
  const [uploadingFile, setUploadingFile] = useState<File>();
  const [progress, setProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
 
  async function uploadFile(file: File) {
    setIsUploading(true);
    setUploadingFile(file);
 
    try {
      // Get presigned URL and final URL from your backend
      const { presignedUrl, fileUrl, fileKey } = await fetch('/api/upload', {
        method: 'POST',
        body: JSON.stringify({
          filename: file.name,
          contentType: file.type,
        }),
      }).then(r => r.json());
 
      // Upload to S3 using presigned URL
      await axios.put(presignedUrl, file, {
        headers: { 'Content-Type': file.type },
        onUploadProgress: (progressEvent) => {
          const progress = (progressEvent.loaded / progressEvent.total) * 100;
          setProgress(progress);
          onUploadProgress?.({ progress });
        },
      });
 
      const uploadedFile = {
        key: fileKey,
        url: fileUrl,
        name: file.name,
        size: file.size,
        type: file.type,
      };
 
      setUploadedFile(uploadedFile);
      onUploadComplete?.(uploadedFile);
      
      return uploadedFile;
    } catch (error) {
      onUploadError?.(error);
      throw error;
    } finally {
      setProgress(0);
      setIsUploading(false);
      setUploadingFile(undefined);
    }
  }
 
  return {
    isUploading,
    progress,
    uploadFile,
    uploadedFile,
    uploadingFile,
  };
}

Examples

Plate UI

Refer to the preview above.

Plate Plus

Plugins

PlaceholderPlugin

Plugin for void media placeholder elements. Handles file uploads, drag & drop, and clipboard paste events.

Options

Collapse all

    Configuration for different file types. Default configuration:

    {
      audio: {
        maxFileCount: 1,
        maxFileSize: '8MB',
        mediaType: AudioPlugin.key,
        minFileCount: 1,
      },
      blob: {
        maxFileCount: 1,
        maxFileSize: '8MB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      image: {
        maxFileCount: 3,
        maxFileSize: '4MB',
        mediaType: ImagePlugin.key,
        minFileCount: 1,
      },
      pdf: {
        maxFileCount: 1,
        maxFileSize: '4MB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      text: {
        maxFileCount: 1,
        maxFileSize: '64KB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      video: {
        maxFileCount: 1,
        maxFileSize: '16MB',
        mediaType: VideoPlugin.key,
        minFileCount: 1,
      },
    }

    Supported file types: 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob'

    Disable empty placeholder when no file is uploading.

    • Default: false

    Disable drag and drop file upload functionality.

    • Default: false

    Maximum number of files that can be uploaded at once, if not specified by uploadConfig.

    • Default: 5

    Allow multiple files of the same type to be uploaded.

    • Default: true

MediaPluginOptions

Plugin options used by media plugins.

Attributes

Collapse all

    A function to check whether a text string is a URL.

    A function to transform the URL.

ImagePlugin

Plugin for void image elements. Options extends MediaPluginOptions.

Options

Collapse all

    Extends MediaPluginOptions.

    An optional method that will upload the image to a server. The method receives either:

    • A data URL (string) from FileReader.readAsDataURL
    • An ArrayBuffer from clipboard data

    Should return either:

    • A URL string to the uploaded image
    • The original data URL/ArrayBuffer if no upload is needed

    If not provided, the original data URL/ArrayBuffer will be used as the image source.

    Disables file upload on data insertion if set to true.

    Disables URL embed on data insertion if set to true.

VideoPlugin

Plugin for void video elements.

AudioPlugin

Plugin for void audio elements.

FilePlugin

Plugin for void file elements.

MediaEmbedPlugin

Plugin for void media embed elements. Options extends MediaPluginOptions.

API Placeholder

editor.tf.insert.media()

Inserts media files into the editor with upload placeholders.

Parameters

Collapse all

    Files to upload. Validates against configured file types and limits.

The transform:

  • Validates files against configured limits (size, count, type)
  • Creates placeholder elements for each file
  • Handles multiple file uploads sequentially
  • Maintains upload history for undo/redo operations
  • Triggers error handling if validation fails

Error codes:

enum UploadErrorCode {
  INVALID_FILE_TYPE = 400,
  TOO_MANY_FILES = 402,
  INVALID_FILE_SIZE = 403,
  TOO_LESS_FILES = 405,
  TOO_LARGE = 413,
}

editor.tf.insert.audioPlaceholder

Inserts a placeholder. Converts to an audio element when completed.

Parameters

Collapse all

    Options for the insert nodes transform.

editor.tf.insert.filePlaceholder

Inserts a placeholder. Converts to a file element when completed.

Parameters

Collapse all

    Options for the insert nodes transform.

editor.tf.insert.imagePlaceholder

Inserts a placeholder. Converts to an image element when completed.

Parameters

Collapse all

    Options for the insert nodes transform.

editor.tf.insert.videoPlaceholder

Inserts a placeholder. Converts to a video element when completed.

Parameters

Collapse all

    Options for the insert nodes transform.

editor.api.placeholder.addUploadingFile()

Tracks a file that is currently being uploaded.

Parameters

Collapse all

    Unique identifier for the placeholder element.

    The file being uploaded.

editor.api.placeholder.getUploadingFile()

Gets a file that is currently being uploaded.

Parameters

Collapse all

    Unique identifier for the placeholder element.

Returns

Collapse all

    The uploading file if found, undefined otherwise.

editor.api.placeholder.removeUploadingFile()

Removes a file from the uploading tracking state after upload completes or fails.

Parameters

Collapse all

    Unique identifier for the placeholder element to remove.

API Media

parseMediaUrl

Parses a media URL and returns the data associated with it based on the configured rules of the media plugin.

Parameters

Collapse all

    The editor instance.

submitFloatingMedia

Submits the floating media element by setting its URL and performing necessary transformations.

Parameters

Collapse all

    The editor instance.

EmbedUrlData

A type defining the data returned from parsing an embed URL.

Attributes

Collapse all

    The URL of the embedded content.

    The provider of the embedded content.

    The unique ID for the embedded content.

    The component to be rendered for the embedded content.

API Image

insertImage

Inserts an image element into the editor.

Parameters

Collapse all

    The editor instance.

    The URL or ArrayBuffer of the image.

    Additional options for inserting the image element.

isImageUrl

Checks if a given URL is a valid image URL.

Parameters

Collapse all

    The URL to check.

withImageUpload

Enhances the editor to support pasting images from the clipboard.

Parameters

Collapse all

    The editor instance.

    The plate plugin.

withImageEmbed

Enhances the editor to automatically insert an image when a URL is pasted.

Parameters

Collapse all

    The editor instance.

    The plate plugin.

API Media Embed

insertMediaEmbed

Inserts a media embed element at the current selection.

Parameters

Collapse all

    The editor instance.

    The options for inserting nodes.

parseIframeUrl

Parses the URL of an iframe embed.

Parameters

Collapse all

    The URL or embed code of the iframe.

parseTwitterUrl

Parses a Twitter URL and extracts the tweet ID.

Parameters

Collapse all

    The Twitter URL.

Returns

Collapse all

    An object containing the tweet ID and provider if the parsing is successful. Returns undefined if the URL is not valid or does not match any supported video providers.

parseVideoUrl

Parses a video URL and extracts the video ID and provider-specific embed URL.

Parameters

Collapse all

    The video URL.

Returns

Collapse all

    An object containing the video ID and provider if the parsing is successful. Returns undefined if the URL is not valid or does not match any supported video providers.

API Components

useResizable

A behavior hook for resizable elements.

State

Collapse all

    The alignment of the content within the resizable element.

    The minimum width that the resizable element can be adjusted to.

    The maximum width that the resizable element can be adjusted to.

    A function to set the width of the node, which is used when resizing.

    A function to set the width of the resizable element directly.

    The current width of the resizable element. This can be a string (e.g., a percentage or 'auto') or a number (representing pixels).

Returns

Collapse all

    A React reference to the outermost div wrapping the resizable element.

    Props to be spread on the outermost div wrapping the resizable element.

    Props to be spread on the resizable element.

useFloatingMediaEditButton

A behavior hook for a floating media edit button.

Returns

Collapse all

useFloatingMediaUrlInput

A behavior hook for a floating media URL input.

Props

Collapse all

    The default value for the URL input field.

Returns

Collapse all

useImage

A behavior hook for an image element.

Returns

Collapse all

useMediaState

A state hook for a media element.

Parameters

Collapse all

Returns

Collapse all

    The alignment of the media element.

    Whether the media element is currently focused.

    Whether the media element is currently selected.

    Whether the editor is in read-only mode.

    The parsed embed data of the media element.

    Whether the media element is a tweet.

    Whether the media element is a video.

    Whether the media element is a YouTube video.

useMediaToolbarButton

A behavior hook for a media toolbar button.

Parameters

Collapse all

Returns

Collapse all

Types

TMediaElement

export interface TMediaElement extends TElement {
  url: string;
  id?: string;
  align?: 'center' | 'left' | 'right';
  isUpload?: boolean;
  name?: string;
  placeholderId?: string;
}

TPlaceholderElement

export interface TPlaceholderElement extends TElement {
  mediaType: string;
}