Use Images
Import Images into the page
Add-ons are hosted in an iframe within the Adobe Express UI, and can load images as <img>
elements like any other web application. But in order to add images into an Adobe Express document, you need to use the addImage()
method of the addOnUISdk.app.document
object.
It expects a Blob
object as the first argument, and an optional MediaAttribute
object with the image's title and author.
Iframe vs. Document Sandbox
The following snippets import the addOnUISdk
object, and belong to the ui/index.js
file. This is the iframe side of the add-on's house—whereas most of the Design Elements how-tos make use of the Document Sandbox API (in the sandbox/code.js
file).
Example
Copied to your clipboard// ui/index.jsimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";addOnUISdk.ready.then(async () => {try {const imageUrl = "https://placehold.co/600x400.png";const image = await fetch(imageUrl);const imageBlob = await image.blob();await addOnUISdk.app.document.addImage(imageBlob, // 👈 Blob object{title: "Placeholder image", // 👈 Optional MediaAttributesauthor: "Adobe Developer",});} catch (e) {console.error("Failed to add the image", e);}});
You can use fetch()
also to get images that are local to the add-on; in this case, paths should be relative to the add-on's root.
Copied to your clipboard// ui/index.jsimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";addOnUISdk.ready.then(async () => {try {const imageUrl = "./600x400.png"; // 👈 Local imageconst image = await fetch(imageUrl);// ... same as before
Image requirements
Please refer to this section to know more about the file formats support and size requirements for images.
Import Animated images
Importing a GIF
via addImage()
won't work as expected, as the method converts the animation into a static image before adding it to the document. You should use the addAnimatedImage()
method instead.
Example
Copied to your clipboard// ui/index.jsimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";addOnUISdk.ready.then(async () => {try {const gifImageUrl = "https://path/to/a/file.gif"; // 👈 a GIF imageconst gifImage = await fetch(gifImageUrl);const gifImageBlob = await gifImage.blob();await addOnUISdk.app.document.addAnimatedImage(// 👈gifImageBlob, // 👈 Blob object{/* ... */} // 👈 Optional MediaAttributes);} catch (e) {console.error("Failed to add the image", e);}});
GIF Image requirements
All image formats are equal, but some formats are more equal than others. Please refer to this FAQ to learn more about specific GIF limitations in terms of size and weight.
Replace Media
The replaceMedia()
method can be used to replace an existing media with a new one. It accepts a single argument of type BitmapImage
.
Compared to the previous examples, replacing media is a method that belongs to the Document Sandbox API (in the sandbox/code.js
file), and not to the iframe (in the ui/index.js
file).
Example
Because the Document Sandbox doesn't have access to the fetch()
function, in the following example the Bitmap data is fetched on the iframe side, and passed to the Document Sandbox to be replaced using the Communication API.
You can copy and paste the following code into a Code Playground session to try it out.
Copied to your clipboard<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Get Started</title></head><body><div class="container"><button id="replace-media-btn" disabled>Replace Media</button></div></body></html>
Copied to your clipboard.container {margin: 24px;display: flex;flex-direction: column;}button {background-color: rgb(82, 88, 228);border-color: rgb(82, 88, 228);border-radius: 16px;border-style: solid;color: rgb(255, 255, 255);font-family: sans-serif;height: 32px;}button:disabled {background-color: rgb(177, 177, 177);border-color: rgb(177, 177, 177);}button:not([disabled]):hover {background-color: rgb(64, 70, 202);cursor: pointer;}
Copied to your clipboardimport addOnUISdk from "https://express.adobe.com/static/add-on-sdk/sdk.js";addOnUISdk.ready.then(async () => {const { runtime } = addOnUISdk.instance;// Get the Document Sandbox proxyconst sandboxProxy = await runtime.apiProxy("documentSandbox");// Get the button and message elementsconst replaceButton = document.getElementById("replace-media-btn");const messageDiv = document.getElementById("message");// Enable the button once everything is readyreplaceButton.disabled = false;// Function to show messages to the userfunction showMessage(text, type = "error") {messageDiv.textContent = text;messageDiv.className = `message ${type}`;messageDiv.style.display = "block";// Hide message after 3 seconds for success, keep error messages visibleif (type === "success") {setTimeout(() => {messageDiv.style.display = "none";}, 3000);}}// Function to replace media in selected containerasync function replaceSelectedMedia() {try {// Hide any previous messagesmessageDiv.style.display = "none";replaceButton.disabled = true;replaceButton.textContent = "Replacing...";// Replace with an image from URLconst imageUrl = "https://placehold.co/800x600.png";// Or replace with a local image:// const imageUrl = "./assets/new-image.jpg";const response = await fetch(imageUrl);const imageBlob = await response.blob();// Pass the blob to the Document Sandbox and get the resultconst result = await sandboxProxy.replaceMediaInSelection(imageBlob);if (result.success) {replaceButton.textContent = "Media Replaced!";showMessage("Media replaced successfully!", "success");setTimeout(() => {replaceButton.textContent = "Replace Selected Media";replaceButton.disabled = false;}, 2000);} else {replaceButton.textContent = "Replace Selected Media";replaceButton.disabled = false;showMessage(result.error);}} catch (error) {console.error("Failed to replace media:", error);replaceButton.textContent = "Replace Selected Media";replaceButton.disabled = false;showMessage("An unexpected error occurred. Please try again.");}}// Add click event listener to the buttonreplaceButton.addEventListener("click", replaceSelectedMedia);});
Copied to your clipboardimport addOnSandboxSdk from "add-on-sdk-document-sandbox";import { editor, constants } from "express-document-sdk";const { runtime } = addOnSandboxSdk.instance;runtime.exposeApi({async replaceMediaInSelection(imageBlob) {try {// Check if there's a selection and it's a MediaContainerNodeif (!editor.context.hasSelection) {return {success: false,error: "No node selected. Please select a MediaContainerNode first."};}const selectedNode = editor.context.selection[0];if (selectedNode.type !== constants.SceneNodeType.mediaContainer) {return {success: false,error: "Selected node is not a MediaContainerNode. Please select an image or media container."};}// Load the new image as a BitmapImageconst bitmapImage = await editor.loadBitmapImage(imageBlob);// Replace the media using queueAsyncEdit since loadBitmapImage is asynceditor.queueAsyncEdit(() => {selectedNode.replaceMedia(bitmapImage);});return {success: true};} catch (error) {console.error("Failed to replace media:", error);return {success: false,error: "Failed to replace media. Please try again."};}}});
Media Node structure
It may be useful to know how Adobe Express represents media nodes in a document, using three Classes:
MediaContainerNode
, the main container
The parent node that displays media within a crop mask. It holds two children:
maskShape
: a FillableNode defining the visible bounds.mediaRectangle
: the actual media (image or video). It also providesreplaceMedia()
to swap content.
MediaRectangleNode
, the abstract base
An abstract base for uncropped, full-frame rectangular media. It can’t be instantiated directly, but defines core properties (width, height, and media data) and shared behavior for positioning, rotation, and sizing.
ImageRectangleNode
, the concrete class
A subclass of MediaRectangleNode
for bitmap images. Created through Editor.createImageContainer()
, it inherits all sizing and positioning features while representing the specific image content.
Copied to your clipboardMediaContainerNode├── maskShape > FillableNode, defines visible area└── mediaRectangle > MediaRectangleNode└── ImageRectangleNode > for images└── UnknownMediaRectangleNode > for other media types
FAQs
Q: How do I add images to a document?
A: Call addOnUISdk.app.document.addImage(blob, attributes)
with image blob and optional MediaAttribute.
Q: What parameters does addImage require?
A: A Blob object is required, MediaAttribute with title and author is optional.
Q: How do I get an image as a blob?
A: Use fetch(imageUrl).then(r => r.blob())
to convert images to blob format.
Q: Can I use local image files?
A: Yes, use relative paths from add-on root with fetch() to load local images.
Q: How do I add animated GIFs?
A: Use addOnUISdk.app.document.addAnimatedImage(blob, attributes)
instead of addImage().
Q: Why doesn't addImage work with GIFs?
A: addImage() converts animations to static images; use addAnimatedImage() to preserve animation.
Q: What image formats are supported?
A: AI, GIF, JPEG, JPG, PNG, PSD, PSDT, and WEBP. 8000px or 80MB.
Q: Are there GIF size limitations?
A: Yes, refer to the FAQ section for specific GIF size and weight limitations.
Q: How do I replace media in an existing MediaContainerNode
?
A: Use the replaceMedia()
method on a MediaContainerNode
with a BitmapImage
object created via Editor.loadBitmapImage()
.
Q: Can I replace any media type with replaceMedia()
?
A: Currently, replaceMedia()
only accepts BitmapImage
objects. The original media can be any type, but replacement must be a static image.