Skip to main content

useEditorImage

The useEditorImage hook from @react-email/editor/plugins creates the image extension used for paste, drop, file-picker, and slash-command uploads.
import { StarterKit } from '@react-email/editor/extensions';
import { useEditorImage } from '@react-email/editor/plugins';

const imageExtension = useEditorImage({
  uploadImage: async (file) => {
    const url = await uploadToStorage(file);
    return { url };
  },
});

const extensions = [StarterKit, imageExtension];
It returns a configured image extension that:
  • adds the image node to the editor schema
  • handles image paste and drop events
  • adds editor.commands.uploadImage()
  • adds editor.commands.setImage(...)
options
UseEditorImageOptions
required
Configuration for the upload flow.

Types

UseEditorImageOptions

interface UseEditorImageOptions {
  uploadImage: (file: File) => Promise<UploadImageResult>;
}
uploadImage
(file: File) => Promise<UploadImageResult>
required
Called after a user pastes, drops, or selects an image file. Resolve with the final hosted image URL.

UploadImageResult

interface UploadImageResult {
  url: string;
}
url
string
required
The final image URL written back to the image node after upload completes.

imageSlashCommand

imageSlashCommand is a ready-made slash command item that triggers editor.commands.uploadImage() after removing the typed slash-command range.
import { imageSlashCommand } from '@react-email/editor/plugins';
import { defaultSlashCommands, SlashCommand } from '@react-email/editor/ui';

<SlashCommand.Root items={[...defaultSlashCommands, imageSlashCommand]} />
The command appears as Image in the slash menu and is categorized under Layout.

Editor commands

Registering useEditorImage() adds two commands to editor.commands.

uploadImage

Opens a native file picker with accept="image/*" and runs the configured upload flow:
editor.commands.uploadImage();
Use this when you want to trigger uploads from your own toolbar buttons or custom UI.

setImage

Inserts an image node directly:
editor.commands.setImage({
  src: 'https://example.com/hero.png',
  alt: 'Hero image',
  width: '600',
  height: '320',
  alignment: 'center',
  href: 'https://react.email',
});
src
string
required
Image source URL.
alt
string
Alternate text for the image.
width
string
default:"'auto'"
Rendered image width.
height
string
default:"'auto'"
Rendered image height.
alignment
'left' | 'center' | 'right'
default:"'center'"
Alignment metadata used by the image UI and serializer.
href
string | null
default:"null"
Optional link URL. When present, the image is wrapped in a link on export.

Upload behavior

All upload entry points share the same behavior:
  1. A temporary blob URL is inserted immediately so the image appears in the editor.
  2. Your uploadImage(file) handler runs.
  3. On success, the blob URL is replaced with the returned url.
  4. On failure, the temporary image node is removed and the error is logged.

EmailEditor integration

If you’re using EmailEditor, you can enable the same behavior without calling useEditorImage() directly:
<EmailEditor
  onUploadImage={async (file) => ({ url: await uploadToStorage(file) })}
/>
See Image Upload for setup patterns and end-to-end examples.