Media
'use client';
import React from 'react';
import { Plate } from '@udecode/plate/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:
- Using our UploadThing implementation
- Creating a custom implementation with your preferred upload solution
UploadThing
-
Add MediaPlaceholderElement component
-
Add API routes for UploadThing:
npx shadcx@latest add plate/api-uploadthing
- Get your secret key from UploadThing for free
- Add your UploadThing secret key to
.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
- Default:
false
- Default:
false
- Default:
5
- Default:
true
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.
Disable drag and drop file upload functionality.
Maximum number of files that can be uploaded at once, if not specified by uploadConfig
.
Allow multiple files of the same type to be uploaded.
MediaPluginOptions
Plugin options used by media plugins.
Attributes
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
- A data URL (string) from
FileReader.readAsDataURL
- An ArrayBuffer from clipboard data
- A URL string to the uploaded image
- The original data URL/ArrayBuffer if no upload is needed
Extends MediaPluginOptions.
An optional method that will upload the image to a server. The method receives either:
Should return either:
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
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
Options for the insert nodes transform.
editor.tf.insert.filePlaceholder
Inserts a placeholder. Converts to a file element when completed.
Parameters
Options for the insert nodes transform.
editor.tf.insert.imagePlaceholder
Inserts a placeholder. Converts to an image element when completed.
Parameters
Options for the insert nodes transform.
editor.tf.insert.videoPlaceholder
Inserts a placeholder. Converts to a video element when completed.
Parameters
Options for the insert nodes transform.
editor.api.placeholder.addUploadingFile()
Tracks a file that is currently being uploaded.
Parameters
Unique identifier for the placeholder element.
The file being uploaded.
editor.api.placeholder.getUploadingFile()
Gets a file that is currently being uploaded.
Parameters
Unique identifier for the placeholder element.
Returns
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
Unique identifier for the placeholder element to remove.
API Media
parseMediaUrl
submitFloatingMedia
EmbedUrlData
API Image
insertImage
isImageUrl
withImageUpload
withImageEmbed
API Media Embed
insertMediaEmbed
Inserts a media embed element at the current selection.
Parameters
The editor instance.
The options for inserting nodes.
parseIframeUrl
Parses the URL of an iframe embed.
Parameters
The URL or embed code of the iframe.
parseTwitterUrl
Parses a Twitter URL and extracts the tweet ID.
parseVideoUrl
Parses a video URL and extracts the video ID and provider-specific embed URL.
API Components
useResizable
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.
Function to set the width of the node when resizing.
Function to set the width of the resizable element directly.
The current width of the resizable element (percentage, 'auto', or pixels).
useFloatingMediaEditButton
useFloatingMediaUrlInput
useImage
useMediaState
A state hook for a media element.
useMediaState
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.
useMediaToolbarButton
Types
TMediaElement
export interface TMediaElement extends TElement {
url: string;
id?: string;
align?: 'center' | 'left' | 'right';
isUpload?: boolean;
name?: string;
placeholderId?: string;
}
TPlaceholderElement
mediaType: string;
}