Image Plugin
The Image plugin provides image upload, embedding, and manipulation capabilities with support for custom upload handlers, image providers, and responsive sizing.Installation
npm install @yoopta/image
Usage
import Image from '@yoopta/image';
import { createYooptaEditor } from '@yoopta/editor';
const plugins = [Image];
const editor = createYooptaEditor({ plugins });
Features
- Image upload with custom handlers
- URL-based image embedding
- Resizable images with width/height control
- Alignment support (left, center, right)
- Object-fit options (contain, cover, fill)
- Upload progress tracking
- Image deletion handlers
- Cloudinary, imgix, and Akamai optimization support
- HTML, Markdown, and Email export
Options
import type { ImagePluginOptions } from '@yoopta/image';
const plugins = [
Image.extend({
options: {
upload: {
// Endpoint-based upload
endpoint: '/api/upload',
method: 'POST',
headers: { 'Authorization': 'Bearer token' },
fieldName: 'file',
maxSize: 5 * 1024 * 1024, // 5MB
onProgress: (progress) => console.log(progress.percentage),
},
delete: {
endpoint: '/api/delete',
method: 'DELETE',
},
maxSizes: {
maxWidth: 1200,
maxHeight: 800,
},
optimizations: {
provider: 'cloudinary',
deviceSizes: [640, 750, 828, 1080, 1200],
},
},
}),
];
Plugin Options Type
type ImagePluginOptions = {
upload?: ImageUploadOptions;
delete?: ImageDeleteOptions;
maxSizes?: {
maxWidth?: number | string;
maxHeight?: number | string;
} | null;
optimizations?: {
deviceSizes?: number[];
provider?: 'imgix' | 'cloudinary' | 'akamai';
} | null;
};
Element Type
import type { ImageElement, ImageElementProps } from '@yoopta/image';
type ImageElementProps = {
id: string | null;
src?: string | null;
alt?: string | null;
srcSet?: string | null;
bgColor?: string | null;
fit?: 'contain' | 'cover' | 'fill' | null;
sizes?: {
width: number | string;
height: number | string;
};
};
type ImageElement = SlateElement<'image', ImageElementProps>;
Default Props
{
id: null,
src: null,
alt: null,
srcSet: null,
bgColor: null,
fit: null,
sizes: { width: 0, height: 0 }
}
Upload Options
Endpoint-Based Upload
import type { ImageUploadEndpointOptions } from '@yoopta/image';
const uploadOptions: ImageUploadEndpointOptions = {
endpoint: '/api/upload',
method: 'POST',
headers: { 'X-API-Key': 'your-key' },
fieldName: 'image',
maxSize: 10 * 1024 * 1024, // 10MB
accept: 'image/png,image/jpeg,image/webp',
onProgress: (progress) => {
console.log(`Uploaded: ${progress.percentage}%`);
},
onSuccess: (result) => {
console.log('Upload complete:', result.url);
},
onError: (error) => {
console.error('Upload failed:', error.message);
},
};
Custom Upload Function
import type { ImageUploadFn } from '@yoopta/image';
const customUpload: ImageUploadFn = async (file, onProgress) => {
// Upload to Cloudinary, S3, Firebase, etc.
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/cloudinary-upload', {
method: 'POST',
body: formData,
});
const data = await response.json();
return {
id: data.public_id,
src: data.secure_url,
alt: file.name,
sizes: {
width: data.width,
height: data.height,
},
};
};
const plugins = [
Image.extend({
options: {
upload: customUpload,
},
}),
];
Hooks
useImageUpload
import { useImageUpload } from '@yoopta/image';
import type { UseImageUploadReturn } from '@yoopta/image';
function ImageUploader({ editor, blockId }) {
const { upload, loading, progress, error, cancel, reset } = useImageUpload(
editor,
blockId
);
const handleUpload = async (file: File) => {
try {
const result = await upload(file);
console.log('Uploaded:', result.url);
} catch (err) {
console.error('Upload failed:', err);
}
};
return (
<div>
{loading && <p>Uploading: {progress?.percentage}%</p>}
{error && <p>Error: {error.message}</p>}
<button onClick={cancel}>Cancel</button>
<button onClick={reset}>Reset</button>
</div>
);
}
useImageDelete
import { useImageDelete } from '@yoopta/image';
import type { UseImageDeleteReturn } from '@yoopta/image';
function ImageDeleter({ editor, blockId, element }) {
const { deleteImage, loading, error } = useImageDelete(editor, blockId);
const handleDelete = async () => {
try {
await deleteImage(element);
console.log('Deleted successfully');
} catch (err) {
console.error('Delete failed:', err);
}
};
return (
<button onClick={handleDelete} disabled={loading}>
{loading ? 'Deleting...' : 'Delete Image'}
</button>
);
}
useImageDimensions
import { useImageDimensions } from '@yoopta/image';
const { width, height, loading } = useImageDimensions(imageUrl);
useImagePreview
import { useImagePreview } from '@yoopta/image';
import type { ImageUploadPreview } from '@yoopta/image';
const preview: ImageUploadPreview = useImagePreview(file);
// { url: string, width?: number, height?: number }
Commands
import { ImageCommands } from '@yoopta/image';
// Insert image
editor.commands.Image.insertImage({
src: 'https://example.com/image.jpg',
alt: 'Description',
sizes: { width: 800, height: 600 },
});
// Update image props
editor.commands.Image.updateImage(blockId, {
fit: 'cover',
sizes: { width: 1200, height: 800 },
});
Parsers
HTML
Deserialize: Converts<img> tags to image blocks
Serialize: Outputs image in flex container for alignment
<div style="
margin-left: 0px;
display: flex;
width: 100%;
justify-content: center;
">
<img
data-meta-align="center"
data-meta-depth="0"
src="https://example.com/image.jpg"
alt="Image description"
width="800"
height="600"
objectFit="contain"
/>
</div>
Markdown

<table style="width:100%;">
<tbody style="width:100%;">
<tr>
<td style="
display: flex;
width: 100%;
justify-content: center;
margin-top: 1rem;
">
<img
src="https://example.com/image.jpg"
alt="Image description"
width="800"
height="600"
style="margin: 0 auto; object-fit: contain;"
/>
</td>
</tr>
</tbody>
</table>
Examples
Basic Image Embed
import { Blocks } from '@yoopta/editor';
import { generateId } from '@yoopta/editor';
Blocks.insertBlock(editor, {
type: 'Image',
value: [
{
id: generateId(),
type: 'image',
props: {
id: 'img-1',
src: 'https://example.com/photo.jpg',
alt: 'Beautiful landscape',
fit: 'cover',
sizes: { width: 1200, height: 800 },
},
children: [{ text: '' }],
},
],
meta: { align: 'center' },
});
Cloudinary Upload Example
import Image from '@yoopta/image';
import type { ImageUploadFn } from '@yoopta/image';
const cloudinaryUpload: ImageUploadFn = async (file, onProgress) => {
const formData = new FormData();
formData.append('file', file);
formData.append('upload_preset', 'your_preset');
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (onProgress && e.lengthComputable) {
onProgress({
loaded: e.loaded,
total: e.total,
percentage: Math.round((e.loaded / e.total) * 100),
});
}
});
const response = await new Promise((resolve, reject) => {
xhr.onload = () => resolve(JSON.parse(xhr.responseText));
xhr.onerror = reject;
xhr.open('POST', 'https://api.cloudinary.com/v1_1/your-cloud/image/upload');
xhr.send(formData);
});
return {
id: response.public_id,
src: response.secure_url,
alt: file.name,
sizes: {
width: response.width,
height: response.height,
},
};
};
const plugins = [
Image.extend({
options: { upload: cloudinaryUpload },
}),
];
Related
- Video Plugin - Video upload and embedding
- File Plugin - File attachments
- Elements API - Manipulate image elements