---
title: Media
docs:
  - route: /docs/components/image-element
    title: Image Element
  - route: /docs/components/media-video-element
    title: Video Element
  - route: /docs/components/media-audio-element
    title: Audio Element
  - route: /docs/components/media-file-element
    title: File Element
  - route: /docs/components/media-embed-element
    title: Media Embed Element
  - route: /docs/components/media-popover
    title: Media Popover
  - route: /docs/components/media-placeholder-element
    title: Media Placeholder Element
  - route: /docs/components/media-upload-toast
    title: Media Upload Toast
  - route: /docs/components/media-toolbar-button
    title: Media Toolbar Button
  - route: https://pro.platejs.org/docs/examples/upload
    title: Upload
  - route: https://pro.platejs.org/docs/components/media-toolbar
    title: Media Toolbar
---

<ComponentPreview name="media-demo" />

<PackageInfo>

## 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

</PackageInfo>

## Installation

```bash
npm install @udecode/plate-media
```

## Usage

```tsx
import {
  AudioPlugin,
  FilePlugin,
  ImagePlugin,
  MediaEmbedPlugin,
  PlaceholderPlugin,
  VideoPlugin,
} from '@udecode/plate-media/react';
import { SelectOnBackspacePlugin } from '@udecode/plate-select';
```

```tsx
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 },
  }),
];
```

```tsx
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](/docs/caption).

### 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](/docs/components/media-placeholder-element) component

2. Add API routes for UploadThing:

<ComponentInstallation name="api-uploadthing" inline />

3. Get your secret key from [UploadThing](https://uploadthing.com/dashboard/settings) for free
4. Add your UploadThing secret key to `.env`:

```bash title=".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:

```ts
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:

```ts
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

<ComponentPreviewPro name="upload-pro" />

## Plugins

### PlaceholderPlugin

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


<APIOptions>
<APIItem name="uploadConfig" type="Partial<Record<AllowedFileType, MediaItemConfig>>" optional>

Configuration for different file types. Default configuration:

```ts
{
  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'`

<APISubList>
  <APISubListItem parent="uploadConfig" name="mediaType" type="MediaKeys">
    The media plugin keys that this config is for: `'audio' | 'file' | 'image' | 'video'`
  </APISubListItem>
  <APISubListItem parent="uploadConfig" name="maxFileCount" type="number" optional>
    The maximum number of files of this type that can be uploaded.
  </APISubListItem>
  <APISubListItem parent="uploadConfig" name="maxFileSize" type="FileSize" optional>
    The maximum file size for a file of this type. Format: `${1|2|4|8|16|32|64|128|256|512|1024}${B|KB|MB|GB}`
  </APISubListItem>
  <APISubListItem parent="uploadConfig" name="minFileCount" type="number" optional>
    The minimum number of files of this type that must be uploaded.
  </APISubListItem>
</APISubList>

</APIItem>

<APIItem name="disableEmptyPlaceholder" type="boolean" optional>
Disable empty placeholder when no file is uploading.

- **Default:** `false`
</APIItem>

<APIItem name="disableFileDrop" type="boolean" optional>
Disable drag and drop file upload functionality.

- **Default:** `false`
</APIItem>

<APIItem name="maxFileCount" type="number" optional>
Maximum number of files that can be uploaded at once, if not specified by `uploadConfig`.

- **Default:** `5`
</APIItem>

<APIItem name="multiple" type="boolean" optional>
Allow multiple files of the same type to be uploaded.

- **Default:** `true`
</APIItem>

</APIOptions>

### MediaPluginOptions

Plugin options used by media plugins.

<APIAttributes>
  <APIItem name="isUrl" type="function" optional>
    A function to check whether a text string is a URL.
  </APIItem>
  <APIItem name="transformUrl" type="function" optional>
    A function to transform the URL.
  </APIItem>
</APIAttributes>

### ImagePlugin

Plugin for void image elements. Options extends [MediaPluginOptions](#mediapluginoptions).

<APIOptions>

Extends [MediaPluginOptions](#mediapluginoptions).

<APIItem name="uploadImage" type="(dataUrl: string | ArrayBuffer) => Promise<string | ArrayBuffer> | string | ArrayBuffer" optional>

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.

</APIItem>
<APIItem name="disableUploadInsert" type="boolean" optional>
Disables file upload on data insertion if set to true.
</APIItem>
<APIItem name="disableEmbedInsert" type="boolean" optional>
Disables URL embed on data insertion if set to true.
</APIItem>
</APIOptions>

### 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.

<APIParameters>
  <APIItem name="files" type="FileList">
    Files to upload. Validates against configured file types and limits.
  </APIItem>
  <APIItem name="options" type="object" optional>
    <APISubList>
      <APISubListItem parent="options" name="at" type="Path" optional>
        Location to insert the media. Defaults to current selection.
      </APISubListItem>
      <APISubListItem parent="options" name="nextBlock" type="boolean" optional>
        Whether to insert a new block after the media.
        - **Default:** `true`
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIParameters>

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:
```ts
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.
<APIParameters>
  <APIItem name="options" type="InsertNodesOptions">
    Options for the insert nodes transform.
  </APIItem>
</APIParameters>


### editor.tf.insert.filePlaceholder

Inserts a placeholder. Converts to a file element when completed.
<APIParameters>
  <APIItem name="options" type="InsertNodesOptions">
    Options for the insert nodes transform.
  </APIItem>
</APIParameters>


### editor.tf.insert.imagePlaceholder

Inserts a placeholder. Converts to an image element when completed.
<APIParameters>
  <APIItem name="options" type="InsertNodesOptions">
    Options for the insert nodes transform.
  </APIItem>
</APIParameters>

### editor.tf.insert.videoPlaceholder

Inserts a placeholder. Converts to a video element when completed.
<APIParameters>
  <APIItem name="options" type="InsertNodesOptions">
    Options for the insert nodes transform.
  </APIItem>
</APIParameters>

### editor.api.placeholder.addUploadingFile()

Tracks a file that is currently being uploaded.

<APIParameters>
  <APIItem name="id" type="string">
    Unique identifier for the placeholder element.
  </APIItem>
  <APIItem name="file" type="File">
    The file being uploaded.
  </APIItem>
</APIParameters>

### editor.api.placeholder.getUploadingFile()

Gets a file that is currently being uploaded.

<APIParameters>
  <APIItem name="id" type="string">
    Unique identifier for the placeholder element.
  </APIItem>
</APIParameters>

<APIReturns>
  <APIItem type="File | undefined">
    The uploading file if found, undefined otherwise.
  </APIItem>
</APIReturns>

### editor.api.placeholder.removeUploadingFile()

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

<APIParameters>
  <APIItem name="id" type="string">
    Unique identifier for the placeholder element to remove.
  </APIItem>
</APIParameters>

## API Media

### parseMediaUrl

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

<APIParameters>
  <APIItem name="editor" type="PlateEditor">
    The editor instance.
  </APIItem>
  <APIItem name="options" type="object">
    <APISubList>
      <APISubListItem parent="options" name="pluginKey" type="string">
        The key of the media plugin.
      </APISubListItem>
      <APISubListItem parent="options" name="url" type="string" optional>
        The URL of the media to be parsed.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIParameters>

### submitFloatingMedia

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

<APIParameters>
  <APIItem name="editor" type="PlateEditor">
    The editor instance.
  </APIItem>
  <APIItem name="options" type="object">
    <APISubList>
      <APISubListItem parent="options" name="element" type="TMediaElement">
        The floating media element to be submitted.
      </APISubListItem>
      <APISubListItem parent="options" name="pluginKey" type="string" optional>
        The key of the media plugin.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIParameters>

### EmbedUrlData

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

<APIAttributes>
  <APIItem name="url" type="string" optional>
    The URL of the embedded content.
  </APIItem>
  <APIItem name="provider" type="string" optional>
    The provider of the embedded content.
  </APIItem>
  <APIItem name="id" type="string" optional>
    The unique ID for the embedded content.
  </APIItem>
  <APIItem name="component" type="React.FC<EmbedUrlData>" optional>
    The component to be rendered for the embedded content.
  </APIItem>
</APIAttributes>

## API Image

### insertImage

Inserts an image element into the editor.

<APIParameters>
  <APIItem name="editor" type="PlateEditor">
    The editor instance.
  </APIItem>
  <APIItem name="url" type="string | ArrayBuffer">
    The URL or ArrayBuffer of the image.
  </APIItem>
  <APIItem name="options" type="InsertNodesOptions" optional>
    Additional options for inserting the image element.
  </APIItem>
  <APISubList>
    <APISubListItem parent="options" name="nextBlock" type="boolean" optional>
      If true, the image will be inserted in the next block.
    </APISubListItem>
  </APISubList>
</APIParameters>

### isImageUrl

Checks if a given URL is a valid image URL.

<APIParameters>
  <APIItem name="url" type="string">
    The URL to check.
  </APIItem>
</APIParameters>

### withImageUpload

Enhances the editor to support pasting images from the clipboard.

<APIParameters>
  <APIItem name="editor" type="E">
    The editor instance.
  </APIItem>
  <APIItem name="plugin" type="PlatePlugin">
    The plate plugin.
  </APIItem>
</APIParameters>

### withImageEmbed

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

<APIParameters>
  <APIItem name="editor" type="E">
    The editor instance.
  </APIItem>
  <APIItem name="plugin" type="PlatePlugin">
    The plate plugin.
  </APIItem>
</APIParameters>

## API Media Embed

### insertMediaEmbed

Inserts a media embed element at the current selection.

<APIParameters>
<APIItem name="editor" type="PlateEditor">
The editor instance.
</APIItem>
<APIItem name="options" type="object">
<APISubList>
<APISubListItem parent="options" name="url" type="string" optional>
The URL of the media embed.

- **Default:** `''`

</APISubListItem>
<APISubListItem
  parent="options"
  name="key"
  type="string"
  optional
>
The key of the media embed element.

- **Default:** `MediaEmbedPlugin.key`

</APISubListItem>
</APISubList>

</APIItem>
<APIItem name="insertNodesOptions" type="InsertNodesOptions">
The options for inserting nodes.
</APIItem>
</APIParameters>

### parseIframeUrl

Parses the URL of an iframe embed.

<APIParameters>
  <APIItem name="url" type="string">
    The URL or embed code of the iframe.
  </APIItem>
</APIParameters>

### parseTwitterUrl

Parses a Twitter URL and extracts the tweet ID.

<APIParameters>
  <APIItem name="url" type="string">
    The Twitter URL.
  </APIItem>
</APIParameters>

<APIReturns>
  <APIItem type="EmbedUrlData | undefined">
    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.
  </APIItem>
</APIReturns>

### parseVideoUrl

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

<APIParameters>
  <APIItem name="url" type="string">
    The video URL.
  </APIItem>
</APIParameters>

<APIReturns>
  <APIItem type="EmbedUrlData | undefined">
    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.
  </APIItem>
</APIReturns>

## API Components

### useResizable

A behavior hook for resizable elements.

<APIState>
  <APIItem name="align" type="'left' | 'center' | 'right'">
    The alignment of the content within the resizable element.
  </APIItem>
  <APIItem name="minWidth" type="ResizeLength">
    The minimum width that the resizable element can be adjusted to.
  </APIItem>
  <APIItem name="maxWidth" type="ResizeLength">
    The maximum width that the resizable element can be adjusted to.
  </APIItem>
  <APIItem name="setNodeWidth" type="function">
    A function to set the width of the node, which is used when resizing.
  </APIItem>
  <APIItem name="setWidth" type="function">
    A function to set the width of the resizable element directly.
  </APIItem>
  <APIItem name="width" type="Property.Width<string | number> | undefined">
    The current width of the resizable element. This can be a string (e.g., a
    percentage or 'auto') or a number (representing pixels).
  </APIItem>
</APIState>

<APIReturns>
  <APIItem name="wrapperRef" type="React.RefObject<HTMLDivElement>">
    A React reference to the outermost div wrapping the resizable element.
  </APIItem>
  <APIItem name="wrapperProps" type="React.HTMLAttributes<HTMLDivElement>">
    Props to be spread on the outermost div wrapping the resizable element.
    <APISubList>
      <APISubListItem parent="wrapperProps" name="style" type="CSSProperties">
        CSS styles to be applied to the wrapper div.
      </APISubListItem>
    </APISubList>
  </APIItem>
  <APIItem name="props" type="object">
    Props to be spread on the resizable element.
    <APISubList>
      <APISubListItem parent="props" name="style" type="CSSProperties">
        CSS styles to be applied to the resizable element.
      </APISubListItem>
    </APISubList>
  </APIItem>
  <APIItem name="context" type="object">
    <APISubList>
      <APISubListItem parent="context" name="onResize" type="function">
        A callback function to be called when the resizable element is resized.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIReturns>

### useFloatingMediaEditButton

A behavior hook for a floating media edit button.

<APIReturns>
  <APIItem name="props" type="object">
    <APISubList>
      <APISubListItem name="onClick" type="function">
        A callback function to handle the button click.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIReturns>

### useFloatingMediaUrlInput

A behavior hook for a floating media URL input.

<APIProps>
  <APIItem name="defaultValue" type="string">
    The default value for the URL input field.
  </APIItem>
</APIProps>

<APIReturns>
  <APIItem name="props" type="object">
    <APISubList>
      <APISubListItem parent="props" name="onChange" type="function">
        A callback function to handle the button click.
      </APISubListItem>
      <APISubListItem parent="props" name="autoFocus" type="boolean">
        If true, the URL input field will be focused on mount.
      </APISubListItem>
      <APISubListItem parent="props" name="defaultValue" type="string">
        The default value for the URL input field.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIReturns>

### useImage

A behavior hook for an image element.

<APIReturns>
  <APIItem name="props" type="object">
    <APISubList>
      <APISubListItem parent="props" name="src" type="string">
        The URL of the media element.
      </APISubListItem>
      <APISubListItem parent="props" name="alt" type="string">
        The caption string for the image.
      </APISubListItem>
      <APISubListItem parent="props" name="draggable" type="boolean">
        Indicates whether the image is draggable.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIReturns>

### useMediaState

A state hook for a media element.

<APIParameters>
<APIItem name="options" type="object">
<APISubList>
<APISubListItem parent="options" name="urlParsers" type="EmbedUrlParser[]" optional>
An array of URL parsers to be used to parse the URL of the media element.

- **`EmbedUrlParser`:** `(url: string) => EmbedUrlData | undefined`

</APISubListItem>
</APISubList>
</APIItem>
</APIParameters>

<APIReturns>
  <APIItem name="align" type="string">
    The alignment of the media element.
  </APIItem>
  <APIItem name="focus" type="boolean">
    Whether the media element is currently focused.
  </APIItem>
  <APIItem name="selected" type="boolean">
    Whether the media element is currently selected.
  </APIItem>
  <APIItem name="readOnly" type="boolean">
    Whether the editor is in read-only mode.
  </APIItem>
  <APIItem name="embed" type="EmbedUrlData">
    The parsed embed data of the media element.
  </APIItem>
  <APIItem name="isTweet" type="boolean">
    Whether the media element is a tweet.
  </APIItem>
  <APIItem name="isVideo" type="boolean">
    Whether the media element is a video.
  </APIItem>
  <APIItem name="isYoutube" type="boolean">
    Whether the media element is a YouTube video.
  </APIItem>
</APIReturns>

### useMediaToolbarButton

A behavior hook for a media toolbar button.

<APIParameters>
  <APIItem name="options" type="object">
    <APISubList>
      <APISubListItem parent="options" name="nodeType" type="string" optional>
        The type of the media node to be inserted.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIParameters>

<APIReturns>
  <APIItem name="props" type="object">
    <APISubList>
      <APISubListItem parent="props" name="onClick" type="function">
        A callback function that inserts the media node into the editor and
        focuses the editor.
      </APISubListItem>
    </APISubList>
  </APIItem>
</APIReturns>

## Types

### TMediaElement

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

### TPlaceholderElement

```tsx
export interface TPlaceholderElement extends TElement {
  mediaType: string;
}
```