@domternal/extension-image
An image node for the Domternal editor: corner-handle
resizing, float/text-wrapping controls (none, left, center, right), paste and
drag-and-drop file upload through your own uploadHandler, a markdown
 input rule, and defense-in-depth XSS validation on src
(blocks javascript:, vbscript:, file:, and non-image data: URLs across
parse, render, command, and input-rule layers). Block-level by default, optional
inline mode.
Links
Website • Documentation • Live examples
Install
pnpm add @domternal/extension-image
@domternal/core and @domternal/pm are peer dependencies (you already have
them if you are building a Domternal editor).
Usage
import { Editor, Document, Paragraph, Text } from '@domternal/core';
import { Image } from '@domternal/extension-image';
const editor = new Editor({
extensions: [
Document,
Paragraph,
Text,
Image.configure({
// Optional: enables paste/drop upload. Return the stored URL.
uploadHandler: async (file) => {
const form = new FormData();
form.append('file', file);
const res = await fetch('/api/upload', { method: 'POST', body: form });
const { url } = (await res.json()) as { url: string };
return url;
},
}),
],
});
// Insert an image
editor.commands.setImage({ src: 'https://example.com/photo.jpg', alt: 'A photo' });
// Wrap text around the selected image
editor.commands.setImageFloat('left');
// Remove the selected image
editor.commands.deleteImage();
Options
Image.configure({ ... }) accepts:
inline(boolean, defaultfalse) - render images inline within paragraphs instead of as block nodes.allowBase64(boolean, defaulttrue) - permitdata:image/URLs; whenfalse, only non-data sources are allowed.uploadHandler((file: File) => Promise<string>ornull, defaultnull) - when set, enables image upload on paste and drop.allowedMimeTypes(string[]) - MIME types accepted for upload (defaults toimage/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif).maxFileSize(number, default0) - max upload size in bytes;0means unlimited.onUploadStart/onUploadError- callbacks fired when an upload begins or fails.HTMLAttributes(Record<string, unknown>) - attributes merged onto the rendered<img>.
Commands
setImage(attributes: SetImageOptions)- insert an image (srcrequired; optionalalt,title,width,height,loading,crossorigin,float).setImageFloat(float: ImageFloat)- set wrapping on the selected image ('none' | 'left' | 'right' | 'center').deleteImage()- delete the selected image.
Editing UI
The user-facing counterpart to the commands above:
- Selecting an image opens a bubble menu with wrapping controls (Inline / Left / Center / Right), an "Edit alt text" action, and Delete.
- The main toolbar and the slash (floating) menu both expose an "Image" action that opens a popover with a URL field and an alt-text field, plus a button to browse for a local file.
- When
uploadHandleris set, pasting or dropping an image file uploads it through the handler and inserts the returned URL. Without anuploadHandler, pasted and dropped images are inlined as base64data:URLs.