Comprehensive media handling capabilities for the Plate rich text editor framework, supporting images, videos, audio files, and embeddable content
—
Advanced placeholder-based upload system with file validation, progress tracking, error handling, and drag & drop support. Provides a comprehensive solution for handling file uploads in rich text editing contexts.
Core plugin providing placeholder functionality for file uploads with validation and progress tracking.
/**
* Base plugin with insert methods for all media types
* Provides placeholder elements that can be replaced with actual media
*/
const BasePlaceholderPlugin: TSlatePlugin<PlaceholderConfig>;
interface PlaceholderConfig extends MediaPluginOptions {
rules?: PlaceholderRule[];
}
interface PlaceholderRule {
/** Media type this rule applies to */
mediaType: string;
}Usage Example:
import { BasePlaceholderPlugin } from "@udecode/plate-media";
import { createSlateEditor } from "@udecode/plate";
const editor = createSlateEditor({
plugins: [
BasePlaceholderPlugin.configure({
options: {
rules: [
{ mediaType: 'image' },
{ mediaType: 'video' },
{ mediaType: 'audio' },
{ mediaType: 'file' }
]
}
})
]
});Functions for inserting placeholder elements of different media types.
/**
* Inserts a placeholder element for the specified media type
* @param editor - Slate editor instance
* @param mediaType - Type of media placeholder to insert
* @param options - Optional insertion configuration
*/
function insertPlaceholder(
editor: SlateEditor,
mediaType: string,
options?: InsertNodesOptions
): void;/**
* Insert image placeholder element
* @param editor - Slate editor instance
* @param options - Optional insertion configuration
*/
function insertImagePlaceholder(
editor: SlateEditor,
options?: InsertNodesOptions
): void;
/**
* Insert video placeholder element
* @param editor - Slate editor instance
* @param options - Optional insertion configuration
*/
function insertVideoPlaceholder(
editor: SlateEditor,
options?: InsertNodesOptions
): void;
/**
* Insert audio placeholder element
* @param editor - Slate editor instance
* @param options - Optional insertion configuration
*/
function insertAudioPlaceholder(
editor: SlateEditor,
options?: InsertNodesOptions
): void;
/**
* Insert file placeholder element
* @param editor - Slate editor instance
* @param options - Optional insertion configuration
*/
function insertFilePlaceholder(
editor: SlateEditor,
options?: InsertNodesOptions
): void;Usage Examples:
import {
insertImagePlaceholder,
insertVideoPlaceholder,
insertPlaceholder
} from "@udecode/plate-media";
// Insert specific placeholder types
insertImagePlaceholder(editor);
insertVideoPlaceholder(editor);
// Insert generic placeholder
insertPlaceholder(editor, 'audio');
insertPlaceholder(editor, 'file', { at: [0, 0] });Function for updating placeholder elements with actual media data after upload.
/**
* Updates a placeholder or media element with new properties
* Used to replace placeholders with actual media after upload completion
* @param editor - Slate editor instance
* @param props - Media properties to set
* @param options - Optional node update configuration
*/
function setMediaNode(
editor: SlateEditor,
props: MediaNodeProps,
options?: SetNodesOptions
): void;
interface MediaNodeProps {
/** Media element type */
type: string;
/** Media URL */
url: string;
/** Optional media ID */
id?: string;
/** Initial height for sizing */
initialHeight?: number;
/** Initial width for sizing */
initialWidth?: number;
/** Whether this is an upload operation */
isUpload?: boolean;
/** File name for downloads */
name?: string;
/** Placeholder ID to replace */
placeholderId?: string;
/** Current width for responsive sizing */
width?: number;
}Usage Example:
import { setMediaNode, insertImagePlaceholder } from "@udecode/plate-media";
// Insert placeholder first
insertImagePlaceholder(editor);
// Later, after upload completes
const uploadResult = await uploadFile(file);
setMediaNode(editor, {
type: 'image',
url: uploadResult.url,
name: uploadResult.fileName,
initialWidth: uploadResult.width,
initialHeight: uploadResult.height,
isUpload: true
});Enhanced React integration with comprehensive file handling, validation, and UI components.
React-enhanced plugin with file drop, paste handling, and upload management.
/**
* Enhanced React plugin with file drop/paste handling and upload management
* Provides comprehensive file validation and progress tracking
*/
const PlaceholderPlugin: TPlatePlugin<PlaceholderConfig>;
interface PlaceholderConfig extends MediaPluginOptions {
/** Disable empty placeholder insertion */
disableEmptyPlaceholder?: boolean;
/** Disable drag & drop functionality */
disableFileDrop?: boolean;
/** File type configuration */
uploadConfig?: UploadConfig;
/** Maximum files allowed globally */
maxFileCount?: number;
/** Allow multiple file selection */
multiple?: boolean;
/** Current upload error */
error?: UploadError | null;
}Comprehensive file type support with validation and limits.
/**
* Configuration mapping for different file types
*/
type UploadConfig = Partial<Record<AllowedFileType, MediaItemConfig>>;
interface MediaItemConfig {
/** Target media type for this file type */
mediaType: MediaKeys;
/** Maximum files allowed for this type */
maxFileCount?: number;
/** Maximum file size (e.g., "4MB", "100KB") */
maxFileSize?: FileSize;
/** Minimum files required */
minFileCount?: number;
}
type AllowedFileType = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob';
type FileSize = `${number}${'B' | 'KB' | 'MB' | 'GB'}`;
type MediaKeys = 'image' | 'video' | 'audio' | 'file';/**
* Array of all supported file type categories
*/
const ALLOWED_FILE_TYPES: AllowedFileType[] = [
'image',
'video',
'audio',
'pdf',
'text',
'blob'
];Usage Example:
import { PlaceholderPlugin, type UploadConfig } from "@udecode/plate-media/react";
const uploadConfig: UploadConfig = {
image: {
mediaType: 'image',
maxFileCount: 10,
maxFileSize: '5MB'
},
video: {
mediaType: 'video',
maxFileCount: 3,
maxFileSize: '50MB'
},
audio: {
mediaType: 'audio',
maxFileCount: 5,
maxFileSize: '10MB'
},
pdf: {
mediaType: 'file',
maxFileCount: 2,
maxFileSize: '25MB'
}
};
const editor = createPlateEditor({
plugins: [
PlaceholderPlugin.configure({
options: {
uploadConfig,
maxFileCount: 20,
multiple: true,
disableFileDrop: false
}
})
]
});Comprehensive error system for upload validation and user feedback.
/**
* Upload error codes enumeration
*/
enum UploadErrorCode {
INVALID_FILE_TYPE = 400,
TOO_MANY_FILES = 402,
INVALID_FILE_SIZE = 403,
TOO_LESS_FILES = 405,
TOO_LARGE = 413,
}
/**
* Discriminated union of upload error types with detailed data structures
*/
type UploadError =
| {
code: UploadErrorCode.INVALID_FILE_TYPE;
data: {
allowedTypes: string[];
files: File[];
};
}
| {
code: UploadErrorCode.TOO_MANY_FILES;
data: {
files: File[];
fileType: AllowedFileType | null;
maxFileCount: number;
};
}
| {
code: UploadErrorCode.INVALID_FILE_SIZE;
data: {
files: File[];
};
}
| {
code: UploadErrorCode.TOO_LESS_FILES;
data: {
files: File[];
fileType: AllowedFileType;
minFileCount: number;
};
}
| {
code: UploadErrorCode.TOO_LARGE;
data: {
files: File[];
fileType: AllowedFileType;
maxFileSize: string;
};
};
/**
* Creates typed upload errors with detailed error data
* @param code - Error code from UploadErrorCode enum
* @param data - Error-specific data structure
* @returns Formatted upload error
*/
function createUploadError<T extends UploadErrorCode>(
code: T,
data: Extract<UploadError, { code: T }>['data']
): UploadError;
/**
* Type guard to check if an unknown value is an UploadError
* @param error - Value to check
* @returns Boolean indicating if value is UploadError
*/
function isUploadError(error: unknown): error is UploadError;Usage Example:
import {
createUploadError,
isUploadError,
UploadErrorCode,
type UploadError
} from "@udecode/plate-media/react";
const handleUploadError = (error: UploadError) => {
switch (error.code) {
case UploadErrorCode.INVALID_FILE_TYPE:
console.error(`Invalid file types. Allowed: ${error.data.allowedTypes.join(', ')}`);
console.error(`Invalid files:`, error.data.files.map(f => f.name));
break;
case UploadErrorCode.TOO_MANY_FILES:
console.error(`Too many files. Maximum allowed: ${error.data.maxFileCount}`);
console.error(`File type: ${error.data.fileType}`);
break;
case UploadErrorCode.INVALID_FILE_SIZE:
console.error(`Invalid file size for files:`, error.data.files.map(f => f.name));
break;
case UploadErrorCode.TOO_LESS_FILES:
console.error(`Not enough ${error.data.fileType} files. Minimum required: ${error.data.minFileCount}`);
console.error(`Current files:`, error.data.files.map(f => f.name));
break;
case UploadErrorCode.TOO_LARGE:
console.error(`Files too large for ${error.data.fileType}. Max size: ${error.data.maxFileSize}`);
console.error(`Large files:`, error.data.files.map(f => f.name));
break;
}
};Comprehensive file validation system with type detection and size checking.
/**
* Validates file selection against upload configuration
* @param files - FileList to validate
* @param config - Upload configuration
* @returns Validation result with errors if any
*/
function validateFiles(
files: FileList,
config: UploadConfig
): { isValid: boolean; error?: UploadError };
/**
* Validates individual file against configuration
* @param file - File to validate
* @param config - Media item configuration
* @returns Validation result
*/
function validateFileItem(
file: File,
config: MediaItemConfig
): { isValid: boolean; error?: UploadError };
/**
* Determines file type category from File object
* @param file - File to categorize
* @returns File type category or undefined if not supported
*/
function matchFileType(file: File): AllowedFileType | undefined;
/**
* Groups files by their detected media type
* @param files - Array of files to group
* @returns Object mapping media types to file arrays
*/
function groupFilesByType(files: File[]): Record<string, File[]>;/**
* Converts file size string to bytes
* @param size - Size string like "4MB", "100KB"
* @returns Size in bytes
*/
function fileSizeToBytes(size: FileSize): number;
/**
* Determines appropriate media type for a file
* @param file - File to analyze
* @returns Corresponding media type
*/
function getMediaType(file: File): MediaKeys;Usage Examples:
import {
validateFiles,
matchFileType,
fileSizeToBytes,
groupFilesByType
} from "@udecode/plate-media/react";
// Validate file selection
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const validation = validateFiles(event.target.files, uploadConfig);
if (!validation.isValid && validation.error) {
setError(validation.error);
return;
}
// Process valid files
processFiles(event.target.files);
}
};
// Check file types
const checkFileType = (file: File) => {
const fileType = matchFileType(file);
console.log(`File type: ${fileType}`); // "image", "video", etc.
};
// Convert file sizes
const maxSizeBytes = fileSizeToBytes("5MB"); // 5242880
// Group files by type
const files = Array.from(fileList);
const grouped = groupFilesByType(files);
// { image: [file1, file2], video: [file3], audio: [file4] }Upload progress and state tracking with React integration.
/**
* Atom store for upload state management
*/
interface PlaceholderStore {
/** Upload operation in progress */
isUploading: boolean;
/** Progress by placeholder ID */
progresses: Record<string, number>;
/** Size constraints for uploads */
size: { width: number; height: number } | null;
/** Files currently being processed */
updatedFiles: File[];
}
/**
* Store access hooks
*/
function usePlaceholderStore(): PlaceholderStore;
function usePlaceholderValue<T>(selector: (store: PlaceholderStore) => T): T;
function usePlaceholderSet(): (updates: Partial<PlaceholderStore>) => void;/**
* Extended API for file tracking during uploads
*/
interface PlaceholderApi {
/** Add file to upload tracking */
addUploadingFile(id: string, file: File): void;
/** Retrieve uploading file by ID */
getUploadingFile(id: string): File | undefined;
/** Remove file from tracking */
removeUploadingFile(id: string): void;
}
/**
* Extended transforms for media insertion
*/
interface PlaceholderTransforms {
/** Insert media from file list */
insertMedia(files: FileList, options?: InsertNodesOptions): void;
}Specialized hooks for placeholder and upload functionality.
/**
* Provides placeholder element state and handlers
* @returns Placeholder element props and interaction handlers
*/
function usePlaceholderElement(): {
element: TPlaceholderElement;
isUploading: boolean;
progress: number;
error: UploadError | null;
};
/**
* Manages placeholder popover interactions
* @returns Popover state and control functions
*/
function usePlaceholderPopover(): {
isOpen: boolean;
open: () => void;
close: () => void;
toggle: () => void;
};Usage Example:
import {
usePlaceholderElement,
usePlaceholderPopover
} from "@udecode/plate-media/react";
const PlaceholderComponent = () => {
const { element, isUploading, progress, error } = usePlaceholderElement();
const popover = usePlaceholderPopover();
return (
<div>
{isUploading && (
<div>
<div>Uploading... {Math.round(progress * 100)}%</div>
<progress value={progress} max={1} />
</div>
)}
{error && (
<div style={{ color: 'red' }}>
Error: {error.message}
</div>
)}
<button onClick={popover.toggle}>
{popover.isOpen ? 'Close' : 'Open'} Upload Options
</button>
{popover.isOpen && (
<div>
{/* Upload options UI */}
</div>
)}
</div>
);
};Comprehensive MIME type definitions organized by category for accurate file type detection.
The upload system supports extensive MIME type mappings:
File type detection uses both MIME type and file extension for maximum compatibility across different browsers and systems.
Install with Tessl CLI
npx tessl i tessl/npm-udecode--plate-media