Images
The image plugin lets users insert images from the web or their local device. The images are inserted using the markdown image syntax, or, if height and width are set, using the HTML img tag. Users can also paste and drop multiple images at once. Optionally, the plugin accepts image URL auto-complete suggestions, and an upload handler that should handle the upload of the pasted and dropped images to a server.
Inserting images with links
The InsertImage toolbar button lets users insert an image from a URL. When the button is clicked, a dialog is shown, where the user can enter the URL of the image or upload one. If the plugin is configured with image URL auto-complete suggestions, they will be shown as the user types.
<MDXEditor
markdown="Hello world"
plugins={[
imagePlugin({
imageUploadHandler: () => {
return Promise.resolve('https://picsum.photos/200/300')
},
imageAutocompleteSuggestions: ['https://picsum.photos/200/300', 'https://picsum.photos/200']
}),
toolbarPlugin({ toolbarContents: () => <InsertImage /> })
]}
/>
Upload progress and retry
While a dialog upload is running, the dialog announces its progress and disables the fields and Save button. Cancel and Escape remain available. If the upload handler rejects or throws, an error is announced and the file, title, and alternative text are kept so the user can retry.
The dialog saves to the editor where it was opened, even if focus or the active editor changes before saving or while uploading. Closing the dialog, opening a different image dialog, making the editor read-only, or unmounting it discards pending dialog results. Replacing the document with setMarkdown dismisses a pending upload and ignores its result, but leaves an idle URL dialog and its fields unchanged; setting the same Markdown leaves uploads running. Imperative insertImage$({ file }) uploads capture the editor at invocation and use the same document, read-only, and unmount guards, but remain independent of any open, closed, or replaced image dialog. These asynchronous guards do not change synchronous { src } insertion: insertImage$ remains usable during plugin postInit and in read-only editors, and saveImage$({ src }) can insert without an open dialog. If an open dialog's target editor has been removed, saving closes it rather than leaving it disabled or silently waiting. Custom dialogs must remain open after publishing saveImage$ with a file until the upload settles; publishing closeImageDialog$ immediately afterwards discards that pending dialog result. Upload failures are logged with console.error, including when using a custom dialog. The imageUploadHandler callback does not accept an abort signal, so dismissing the dialog does not cancel the network request itself.
Custom image dialogs can read imageUploadState$ (idle, uploading, or error). The built-in status messages can be translated using uploadImage.uploading and uploadImage.uploadError. These states describe dialog uploads; pasted and dropped uploads retain their existing behavior.
Pasting and dropping images
The editor handles dropping and pasting images and clipboard contents that contain images. To handle that, you need to upload the image to a location of your choice and return the URL of the uploaded image. This is done through the imageUploadHandler parameter of the imagePlugin. The parameter accepts a function that receives a File object and returns a Promise that resolves to the URL of the uploaded image.
async function imageUploadHandler(image: File) {
const formData = new FormData()
formData.append('image', image)
// send the file to your server and return
// the URL of the uploaded image in the response
const response = await fetch('/uploads/new', {
method: 'POST',
body: formData
})
const json = (await response.json()) as { url: string }
return json.url
}
;<MDXEditor markdown="Hello world" plugins={[imagePlugin({ imageUploadHandler })]} />
Image resizing
The built-in markdown image syntax does not support explicit image dimensions. To work around that, the image plugin resorts to serializing images as HTML img tags with width and height attributes if the user resizes an image or if the markdown input includes an img tag with those attributes set.
const markdown = `
Image without dimensions:

Image with dimensions:
<img src="https://picsum.photos/200/300" width="100" height="150" />
`
<MDXEditor
markdown={markdown}
plugins={[
imagePlugin({ imageUploadHandler })
]}
/>