0
# Transforms and Utilities
1
2
Functions for programmatically inserting images and utility functions for image URL validation.
3
4
## Capabilities
5
6
### Image Insertion Transform
7
8
Function for programmatically inserting image elements into the Plate editor.
9
10
```typescript { .api }
11
/**
12
* Inserts an image element at the current cursor position
13
* Creates a properly structured image element with the provided URL
14
* @param editor - Plate editor instance
15
* @param url - Image URL (string) or data (ArrayBuffer) to insert
16
*/
17
function insertImage<V extends Value>(
18
editor: PlateEditor<V>,
19
url: string | ArrayBuffer
20
): void;
21
```
22
23
**Element Creation:**
24
- Creates `TImageElement` with proper type and structure
25
- Sets URL from provided parameter
26
- Adds empty text child node (required by Slate)
27
- Inserts at current selection or cursor position
28
29
**Usage Examples:**
30
31
```typescript
32
import { insertImage } from "@udecode/plate-image";
33
import { useEditorRef } from "@udecode/plate-core";
34
35
// Insert image from URL
36
function InsertImageButton({ imageUrl }: { imageUrl: string }) {
37
const editor = useEditorRef();
38
39
const handleInsert = () => {
40
insertImage(editor, imageUrl);
41
};
42
43
return (
44
<button onClick={handleInsert}>
45
Insert Image
46
</button>
47
);
48
}
49
50
// Insert multiple images
51
function InsertImageGallery({ imageUrls }: { imageUrls: string[] }) {
52
const editor = useEditorRef();
53
54
const insertGallery = () => {
55
imageUrls.forEach((url, index) => {
56
// Insert with slight delay to ensure proper positioning
57
setTimeout(() => {
58
insertImage(editor, url);
59
}, index * 100);
60
});
61
};
62
63
return (
64
<button onClick={insertGallery}>
65
Insert Gallery ({imageUrls.length} images)
66
</button>
67
);
68
}
69
70
// Insert image from file upload
71
function FileUploadImageInserter() {
72
const editor = useEditorRef();
73
74
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
75
const file = event.target.files?.[0];
76
if (file && file.type.startsWith('image/')) {
77
const reader = new FileReader();
78
reader.onload = (e) => {
79
if (e.target?.result) {
80
insertImage(editor, e.target.result as string);
81
}
82
};
83
reader.readAsDataURL(file);
84
}
85
};
86
87
return (
88
<input
89
type="file"
90
accept="image/*"
91
onChange={handleFileUpload}
92
/>
93
);
94
}
95
96
// Insert with custom image processing
97
async function insertProcessedImage(
98
editor: PlateEditor,
99
rawImageData: string | ArrayBuffer
100
) {
101
try {
102
// Process or upload image first
103
const processedUrl = await processImageData(rawImageData);
104
insertImage(editor, processedUrl);
105
} catch (error) {
106
console.error('Failed to process image:', error);
107
// Fallback to raw data
108
insertImage(editor, rawImageData);
109
}
110
}
111
```
112
113
### Image URL Validation Utility
114
115
Utility function to determine if a given URL points to an image file based on file extension.
116
117
```typescript { .api }
118
/**
119
* Checks if a URL points to an image file
120
* Validates both URL format and file extension against comprehensive image format list
121
* @param url - URL string to validate
122
* @returns Boolean indicating whether URL is a valid image URL
123
*/
124
function isImageUrl(url: string): boolean;
125
```
126
127
**Validation Process:**
128
1. **URL Format**: Checks if string is a valid URL using `isUrl` helper
129
2. **Extension Extraction**: Extracts file extension from URL pathname
130
3. **Format Check**: Compares extension against comprehensive list of image formats
131
132
**Supported Image Formats:**
133
- **Common formats**: jpg, jpeg, png, gif, webp, svg, bmp, tiff, ico
134
- **Raw formats**: cr2, raw, dng, nef, arw
135
- **Professional formats**: psd, ai, eps, xcf
136
- **Specialized formats**: Many additional formats (120+ total)
137
138
**Usage Examples:**
139
140
```typescript
141
import { isImageUrl } from "@udecode/plate-image";
142
143
// Basic URL validation
144
const urls = [
145
'https://example.com/image.jpg', // true
146
'https://example.com/image.png', // true
147
'https://example.com/document.pdf', // false
148
'not-a-url', // false
149
'https://example.com/image.webp', // true
150
];
151
152
urls.forEach(url => {
153
console.log(`${url}: ${isImageUrl(url)}`);
154
});
155
156
// Use in paste handler
157
function handlePaste(event: ClipboardEvent) {
158
const text = event.clipboardData?.getData('text/plain');
159
160
if (text && isImageUrl(text)) {
161
console.log('Pasted text is an image URL:', text);
162
insertImage(editor, text);
163
event.preventDefault();
164
}
165
}
166
167
// Filter image URLs from mixed content
168
function filterImageUrls(urls: string[]): string[] {
169
return urls.filter(isImageUrl);
170
}
171
172
// Validate before insertion
173
function safeInsertImage(editor: PlateEditor, url: string) {
174
if (isImageUrl(url)) {
175
insertImage(editor, url);
176
return true;
177
} else {
178
console.warn('Invalid image URL:', url);
179
return false;
180
}
181
}
182
183
// Custom validation with fallback
184
function validateAndInsertImage(editor: PlateEditor, url: string) {
185
if (isImageUrl(url)) {
186
insertImage(editor, url);
187
} else {
188
// Try to detect image by MIME type or other means
189
fetch(url, { method: 'HEAD' })
190
.then(response => {
191
const contentType = response.headers.get('content-type');
192
if (contentType?.startsWith('image/')) {
193
insertImage(editor, url);
194
} else {
195
console.error('URL does not point to an image');
196
}
197
})
198
.catch(error => {
199
console.error('Failed to validate URL:', error);
200
});
201
}
202
}
203
```
204
205
## Advanced Transform Patterns
206
207
### Batch Image Operations
208
209
```typescript
210
import { insertImage, isImageUrl } from "@udecode/plate-image";
211
import { Transforms } from "slate";
212
213
// Insert multiple images with spacing
214
async function insertImageBatch(
215
editor: PlateEditor,
216
urls: string[],
217
spacing: boolean = true
218
) {
219
const validUrls = urls.filter(isImageUrl);
220
221
for (let i = 0; i < validUrls.length; i++) {
222
insertImage(editor, validUrls[i]);
223
224
if (spacing && i < validUrls.length - 1) {
225
// Insert paragraph between images
226
Transforms.insertNodes(editor, {
227
type: 'p',
228
children: [{ text: '' }]
229
});
230
}
231
}
232
}
233
234
// Replace selected content with image
235
function replaceSelectionWithImage(editor: PlateEditor, url: string) {
236
if (isImageUrl(url)) {
237
// Delete current selection
238
if (editor.selection) {
239
Transforms.delete(editor);
240
}
241
insertImage(editor, url);
242
}
243
}
244
```
245
246
### Integration with Upload Systems
247
248
```typescript
249
// Insert image with upload progress tracking
250
async function insertImageWithUpload(
251
editor: PlateEditor,
252
file: File,
253
onProgress?: (percent: number) => void
254
) {
255
try {
256
// Create temporary data URL for immediate display
257
const reader = new FileReader();
258
reader.onload = (e) => {
259
if (e.target?.result) {
260
insertImage(editor, e.target.result as string);
261
}
262
};
263
reader.readAsDataURL(file);
264
265
// Upload file and replace with final URL
266
const uploadedUrl = await uploadWithProgress(file, onProgress);
267
268
// Replace temporary image with uploaded version
269
// (Implementation would need to track and update the specific image)
270
271
} catch (error) {
272
console.error('Upload failed:', error);
273
}
274
}
275
276
// Utility for drag-and-drop integration
277
function handleImageDrop(
278
editor: PlateEditor,
279
event: DragEvent,
280
uploadFunction?: (file: File) => Promise<string>
281
) {
282
event.preventDefault();
283
284
const files = Array.from(event.dataTransfer?.files || []);
285
const imageFiles = files.filter(file => file.type.startsWith('image/'));
286
287
imageFiles.forEach(async (file) => {
288
if (uploadFunction) {
289
try {
290
const url = await uploadFunction(file);
291
insertImage(editor, url);
292
} catch (error) {
293
// Fallback to data URL
294
const reader = new FileReader();
295
reader.onload = (e) => {
296
if (e.target?.result) {
297
insertImage(editor, e.target.result as string);
298
}
299
};
300
reader.readAsDataURL(file);
301
}
302
} else {
303
// Direct data URL insertion
304
const reader = new FileReader();
305
reader.onload = (e) => {
306
if (e.target?.result) {
307
insertImage(editor, e.target.result as string);
308
}
309
};
310
reader.readAsDataURL(file);
311
}
312
});
313
}
314
```
315
316
### Error Handling and Validation
317
318
```typescript
319
// Robust image insertion with validation and error handling
320
async function safeInsertImage(
321
editor: PlateEditor,
322
urlOrData: string | ArrayBuffer,
323
options?: {
324
validateUrl?: boolean;
325
maxSize?: number;
326
allowedFormats?: string[];
327
}
328
) {
329
try {
330
// Validate URL if it's a string
331
if (typeof urlOrData === 'string') {
332
if (options?.validateUrl && !isImageUrl(urlOrData)) {
333
throw new Error('Invalid image URL format');
334
}
335
336
// Optional: Check if URL is accessible
337
if (options?.validateUrl) {
338
const response = await fetch(urlOrData, { method: 'HEAD' });
339
if (!response.ok) {
340
throw new Error(`Image URL not accessible: ${response.status}`);
341
}
342
}
343
}
344
345
// Insert the image
346
insertImage(editor, urlOrData);
347
return true;
348
349
} catch (error) {
350
console.error('Failed to insert image:', error);
351
352
// Optional: Show user-friendly error message
353
if (typeof urlOrData === 'string') {
354
console.log('Failed URL:', urlOrData);
355
}
356
357
return false;
358
}
359
}
360
```