Embed SDK Generate Image tutorial
Learn how to implement the Generate Image module using the Adobe Express Embed SDK.
Introduction
Welcome to this hands-on tutorial! We'll walk you through implementing the powerful Generate Image module of the Adobe Express Embed SDK. By the end, your integration will be able to use all its new V2 features, from the Community Wall to Rich Preview and Thumbnail actions. As a bonus, we'll implement a Custom version of the Community Wall, so that you can showcase your own images instead of Firefly's publicly available gallery.
What you'll learn
By completing this tutorial, you'll gain practical skills in:
- Implementing the Generate Image module with the Adobe Express Embed SDK.
- Using the new V2 features of the Generate Image module.
- Implementing the Custom Community Wall.
What you'll build
You'll build a simple, JavaScript-based web application that allows users to generate images from text prompts using the Generate Image module of the Adobe Express Embed SDK.
Prerequisites
Before you start, make sure you have:
- An Adobe account (use your existing Adobe ID or create one for free)
- API credentials from the Adobe Developer Console (Get credentials)
- Basic knowledge of HTML, CSS, and JavaScript
- Node.js installed on your development machine (v20.19.0 or higher)
- A text editor or IDE of your choice
1. Set up the project
1.1 Clone the sample
You can start by cloning the Embed SDK Generate Image sample from GitHub and navigating to the project directory.
Copied to your clipboardgit clone https://github.com/AdobeDocs/embed-sdk-samples.gitcd embed-sdk-samples/code-samples/tutorials/embed-sdk-generate-image
The project will have a structure like this:
Copied to your clipboard.├── package.json 📦 Project configuration├── vite.config.js 🔧 Build configuration└── src├── images 📷 Images for the Custom Community Wall│ └── ...├── index.html 🌐 UI container├── main.js 💻 Embed SDK logic├── community-wall.js 🖼️ Custom Community Wall logic└── style.css 🎨 CSS styles
1.2 Set up the API key
Locate the src/.env
file and replace the placeholder string in the VITE_API_KEY
with your Embed SDK API Key:
Copied to your clipboardVITE_API_KEY="your-api-key-here!"
📖 Instructions on how to obtain an API Key can be found on the Quickstart Guide. Make sure your API Key is set to allow the localhost:5555
domain and port.
1.3 Install dependencies
Install the dependencies by running the following commands:
Copied to your clipboardnpm installnpm run start
The web application will be served at localhost:5555
on a secure HTTPS connection—which is always required for any Embed SDK integration. Open your browser and navigate to this address to see it in action.
When clicking the Generate Image button, the Adobe Express Generate Image module will launch, showing the Community Wall—optionally, populated with your own pictures. Users can browse the gallery, select an image, and use its prompt as a starting point for their own generation.
The sample project will handle the file transfer between Adobe Express and the web page hosting it, and the generated image will be displayed in lieu of the placeholder.
Error: "Adobe Express is not available"
In case you get a popup when trying to launch the Adobe Express integration with the following message: "You do not have access to this service. Contact your IT administrator to gain access", please check to have entered the correct API Key in the src/.env
file as described here.
2. Load the Generate Image v2 module
You can just read the existing code in the sample, but it's always best to learn by doing! We suggest following along and typing the code in—even small mistakes can lead to important discoveries.
The sample project is a simple web application built with Vite, which takes care of the entire local HTTPS setup and hot reloading.
2.1 Import the Embed SDK
In this tutorial, we'll focus on the JavaScript side of things first—the HTML content is not overly important. Open the project in your code editor of choice. In main.js
, remove everything below the Spectrum import
statements—we'll rebuild it from scratch.
Copied to your clipboard// main.js// Import theme and typography styles from Spectrum Web Componentsimport "@spectrum-web-components/styles/typography.css";import "@spectrum-web-components/theme/express/theme-light.js";import "@spectrum-web-components/theme/express/scale-medium.js";import "@spectrum-web-components/theme/sp-theme.js";// Import Spectrum Web Componentsimport "@spectrum-web-components/button/sp-button.js";import "@spectrum-web-components/button-group/sp-button-group.js";import "@spectrum-web-components/divider/sp-divider.js";import "./style.css";
The imports above allow us to style our web application with Spectrum Web Components and the Adobe Express theme. Let's begin working in main.js
by importing the Embed SDK:
Copied to your clipboard// main.js//... previous imports ...// Import the Adobe Express Embed SDKawait import("https://cc-embed.adobe.com/sdk/v4/CCEverywhere.js");console.log("CCEverywhere loaded", window.CCEverywhere);
There are several ways to import CCEverywhere.js
: for more information, please refer to the Quickstart Guide.
2.2 Initialize the Embed SDK
When the Embed SDK is imported, a CCEverywhere
object is globally available and must be initialized. There are two sets of parameters that you can pass as option objects:
- Host Information: containing the API key, Application name, etc.
- Configuration: optional settings, like locale, delayed sign-in, etc.
Copied to your clipboard// main.js//... previous imports ...// 👀 Required parameters to initialize the Embed SDKconst hostInfo = {clientId: import.meta.env.VITE_API_KEY,appName: "Embed SDK Sample",};// Optional parametersconst configParams = { /* ... */ };// Initialize the Adobe Express Embed SDK// Destructure the `module` property onlyconst { module } = await window.CCEverywhere.initialize(hostInfo,configParams);
The hostInfo
object is required: the clientId
contains your API Key (here, retrieved by Vite from the .env
file) and the appName
.
The appName
must match the name of your application, and it will be displayed in the Adobe Express UI as a folder where users can store their documents. All configParams
are optional.
2.3 Load the module
The asynchronous CCEverywhere.initialize()
method returns an object with three properties. Here, we destructure the module
only, because it is the entry point to the createImageFromText()
method. In the next section, we'll learn how to use it to launch the Generate Image experience.
Copied to your clipboardmodule.createImageFromText({ /* ... */ });
3. Launch the Generate Image experience
The createImageFromText()
method expects three optional parameters:
Copied to your clipboard// module.createImageFromText() function signatureconst appConfig = { /* ... */ }; // Text to Image experienceconst exportConfig = { /* ... */ }; // Export optionsconst containerConfig = { /* ... */ }; // SDK containermodule.createImageFromText(appConfig, exportConfig, containerConfig);
In this tutorial, we'll focus on the appConfig
object; you can look at the Full Editor tutorial for more details on the other two parameters.
3.1 Enable the v2 experience
The first thing we need to do is to enable the v2 experience.
Copied to your clipboard// main.js//... previous code ...const appConfig = {appVersion: "2",// ...};
You can find a comprehensive list of all the new features of the Generate Image v2 experience in this Concepts guide.
3.2 Toggle selected v2 features
Among all the new features, here we'll focus on the following ones:
- Community Wall, the endless gallery of images generated by other users that are displayed when the experience is launched.
- Rich Preview, a larger preview of the generated image in the thumbnail.
- Thumbnail actions, a dropdown menu in the thumbnail to allow users to further edit the generated image.
Copied to your clipboard// main.js//... previous code ...const appConfig = {appVersion: "2",featureConfig: {"community-wall": true, // 👈 Enable the Community Wall},thumbnailOptions: ["rich-preview", // 👈 Enable the Rich Preview"edit-dropdown", // 👈 Enable the Edit dropdown],editDropdownOptions: [ // 👇 Set the options for the Edit dropdown{ option: "add-effects" },{ option: "remove-background" },{ option: "apply-adjustment" },{ option: "insert-object" },{ option: "remove-object" },],};
Prompt and Community Wall are mutually exclusive.
In the appConfig
object, you can pass a promptText
string property to start generating images right away, as the experience is launched. Please note that adding a prompt will disable the Community Wall—it's either one or the other.
3.3 Connect the UI with the Embed SDK logic
The last step is to connect the UI in index.html
, a simple button and an image placeholder, with the Embed SDK logic in main.js
.
In the UI:
- The
generateBtn
button is used to launch the Generate Image experience. - An
<img>
element is used to display the generated image when it's ready.
In main.js
:
- The
module.createImageFromText()
method is called when thegenerateBtn
button is clicked. - A
callbacks
object is used to handle the experience lifecycle.onPublish()
is called when the user publishes the generated image. ThepublishParams
object contains aprojectId
and anasset
object with the generated image data, that we've used to set thesrc
attribute of the<img>
element.
- The
exportConfig
array is used to set the export options available to the user. Here, we've added two buttons:download
: it allows users to download the generated image.save-modified-asset
: it saves the generated image and passes it to theonPublish()
callback.
Copied to your clipboard// Import theme and typography styles from Spectrum Web Componentsimport "@spectrum-web-components/styles/typography.css";import "@spectrum-web-components/theme/express/theme-light.js";import "@spectrum-web-components/theme/express/scale-medium.js";import "@spectrum-web-components/theme/sp-theme.js";// Import Spectrum Web Componentsimport "@spectrum-web-components/button/sp-button.js";import "@spectrum-web-components/button-group/sp-button-group.js";import "@spectrum-web-components/divider/sp-divider.js";import "./style.css";// Import the Adobe Express Embed SDKawait import("https://cc-embed.adobe.com/sdk/v4/CCEverywhere.js");console.log("CCEverywhere loaded", window.CCEverywhere);// Parameters for initializing the Adobe Express Embed SDKconst hostInfo = {clientId: import.meta.env.VITE_API_KEY,appName: "Embed SDK Sample",};// Initializing the Adobe Express Embed SDKconst { module } = await window.CCEverywhere.initialize(hostInfo, {});// Will hold the project ID when a document is saved on Adobe Expressvar existingProjectId = null;var expressImage = document.getElementById("savedImage");// Callbacks to be used when creating or editing a documentconst callbacks = {onCancel: () => {},onPublish: (intent, publishParams) => {existingProjectId = publishParams.projectId;console.log("Project ID", existingProjectId);expressImage.src = publishParams.asset[0].data;console.log("Image data", publishParams.asset[0].data);},onError: (err) => {console.error("Error!", err.toString());},};// Configuration for the app, shared by both Create and Edit flowsconst appConfig = {appVersion: "2",featureConfig: { "community-wall": true },thumbnailOptions: ["rich-preview","edit-dropdown"],editDropdownOptions: [{ option: "add-effects" },{ option: "remove-background" },{ option: "apply-adjustment" },{ option: "insert-object" },{ option: "remove-object" },],callbacks,};// Configuration for the export options made available to the user// when creating or editing a documentconst exportConfig = [{id: "download", label: "Download",action: { target: "download" }, style: { uiType: "button" }},{id: "save-modified-asset", label: "Save image",action: { target: "publish" }, style: { uiType: "button" }},];// Click handler for the Create Design buttondocument.getElementById("generateBtn").onclick = async () => {module.createImageFromText(appConfig, exportConfig);};
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>Embed SDK Sample</title></head><body><sp-theme scale="medium" color="light" system="express"><div class="container"><header><h1>Adobe Express Embed SDK</h1><sp-divider size="l"></sp-divider><h2>Generate Image Sample</h2><p>The <b>Generate Image</b> button generates an image from the current project.</p></header><main><img id="savedImage" src="https://placehold.co/300x300?text=Placeholder+Image&font=source-sans-pro"alt="Your design will appear here." /><sp-button-group><sp-button id="generateBtn">Generate Image</sp-button></sp-button-group></main></div></sp-theme><script type="module" src="./main.js"></script></body></html>
3.4 Deal with new Intents
When adding the Edit dropdown menu to each generated image (via the "edit-dropdown"
option in the thumbnailOptions
array, and selecting specific editDropdownOptions
), users will be allowed to further edit the image. Technically, they will jump from the Generate Image to the Edit Image experience.
Whenever users transition from one design workflow to another, Embed SDK will invoke the onIntentChange()
callback to inform you about the change. You can use this opportunity to configure the new intent by passing appConfig
, exportConfig
, or any other relevant settings. These configurations are applied while launching the new intent; if missing, the default settings will be used.
The callback receives two parameters, the oldIntent
and the newIntent
, both of type ActionIntent
. In our specific case, the oldIntent
will be ModuleIntent.CREATE_IMAGE_FROM_TEXT
and the newIntent
will be a ModuleIntent.EDIT_IMAGE
.
Copied to your clipboard// main.js//... previous code ...const callbacks = {onCancel: () => { /* ... */ },onPublish: (intent, publishParams) => { /* ... */ },onError: (err) => { /* ... */ },// 👇onIntentChange: (oldIntent, newIntent) => {console.log("Intent changed from", oldIntent, "to", newIntent);return {appConfig: { /* ... */ },exportConfig: [ /* ... */ ],containerConfig: { /* ... */ }}},};
You may want to use the onIntentChange()
callback to, say, configure different Export buttons that better suit the new intent. Please note that subscribing to the callback is totally optional.
4. Customize the Community Wall
The Community Wall is able to showcase custom images instead of the Firefly's publicly available gallery. This is ideal if your users need to generate specific subjects (say, sports team's crests, logos, etc.), and would appreciate a more relevant starting point.
4.1 Enable the Custom Community Wall
To enable the Custom Community Wall, you need to supply a communityWallConfig
object to appConfig
when you launch Generate Image v2.
Copied to your clipboardconst appConfig = {appVersion: "2",featureConfig: {"community-wall": true, // 👈 turn the wall on},communityWallConfig: {// 👇 your own data-loaderfetchCommunityAssets: myFetchCommunityAssets,},};
4.2 Provide your own data-loader
The fetchCommunityAssets
property is nothing but a callback function that you need to implement, designed to fetch a list of your assets in a paginated way.
Pagination, that must be supported on your backend, allows the module to load data in smaller chunks as users scroll, reducing load times and improving the overall experience. As they keep scrolling through the Community Wall, the module will repeatedly invoke fetchCommunityAssets
to get the next set of images.
The function is called with a limit
and cursor
parameters. The limit
is the number of assets to retrieve at any given time, and the cursor
is used to keep track of the batch of assets to fetch next.
The cursor
defaults to "Start_Page"
on the first call, and you can send "Last_Page"
when there are no more custom assets to fetch. At that point, the module will stop asking for more data.
The fetchCommunityAssets
callback signature
Copied to your clipboardasync function myFetchCommunityAssets(limit: number,cursor: string // "Start_Page" on the first call, "Last_Page" on the last call): Promise<CommunityWallAssetReponse> {/* …fetch and return your data… */}
The fetchCommunityAssets
asynchronous callback should return a Promise that resolves to a CommunityWallAssetReponse
object.
Copied to your clipboardinterface CommunityWallAssetReponse {assets: CommunityWallAssetData[];cursor: string; // The cursor for the next page of assets.}
The Asset schema
The assets
array contains the asset objects to display in the Community Wall.
Copied to your clipboardinterface CommunityWallAssetData {assetId: string; // Asset ID for the community assettitle: string; // Prompt for the thumbnail itemheight: number; // Height of the thumbnail imagewidth: number; // Width of the thumbnail imagethumbnailSrc: string; // Source of the thumbnail image (base64 string)fullRenditionSrc: string; // Source of the full rendition image// for one-up view (base64 string)ownerInfo?: { // Optional: Information about the ownername: string; // Owner's display nameimgSrc: string; // Source URL of the owner's image};}
The thumbnailSrc
is the smaller image, used to display in the Community Wall, while the fullRenditionSrc
is the larger image, used to display in the OneUp view. Both must be base64 encoded strings.
For more information about the aspect ratio of the assets, please refer to the Generate Image v2 Concepts guide.
4.3 Implement the fetchCommunityAssets
callback
Now that we know how to provide assets for the Custom Community Wall, let's implement it in our sample project. As follows, the final result.
In the src/images
folder, you'll find 24 images of cats enjoying some leisure time that have been generated with Adobe Firefly, alongside with an images.json
file.
Copied to your clipboard// images/images.json[{"file": "Firefly_A cat on a leather armchair sipping various cocktails 232431.jpg","prompt": "A cat on a leather armchair sipping various cocktails"},{"file": "Firefly_A cat on a leather armchair sipping various cocktails 349610.jpg","prompt": "A cat on a leather armchair sipping various cocktails"},...]
The file
property is the name of the image file, and the prompt
is the prompt used to generate it. We'll use this file to fetch the assets for the Custom Community Wall.
We've created a separate community-wall.js
module to handle everything.
- The
"uuid"
module (the only external dependency) is a handy helper to assign a uniqueassetId
to every thumbnail we feed to the Community Wall. fetchImages()
: grabsimages/images.json
, the tiny manifest that pairs each cat photo with the prompt that generated it. The function returns the parsed JSON or throws if the file can’t be reached.fileToBase64()
: fetches an individual image file, converts it to a Base-64 data URL withFileReader
, and resolves a promise once the conversion is done. This is the heavy-lifting step that turns our static JPGs into strings the Community Wall, can digest.- We implemented a one-time memory cache to avoid hitting the network twice for the same file.
_cache
: stores{ prompt, base64 }
pairs so we never hit the network twice for the same file._cursor
: remembers how many images we’ve already served to enable simple, stateless pagination.PAGE_SIZE
: set to12
, mirroring Firefly’s default number of wall tiles per “page.”hydrateCache()
: lazily builds the cache on first use. It reads the manifest, converts every file to Base-64, then stashes the result in_cache
. Subsequent calls return the already-hydrated data immediately.
fetchCommunityAssets()
is the core function that the Embed SDK actually invokes.- It first ensures the cache is loaded (
hydrateCache
). - It resets
_cursor
when it reaches the end so a fresh Embed SDK session can still show the wall (otherwise it will try to load non-existing images). - It slices the next
PAGE_SIZE
items out of the cache. - It maps each slice entry into the schema the SDK expects, adding a freshly minted
uuidv4()
asassetId
. Thumbnail dimensions are hard coded for simplicity. - It advances
_cursor
, then returns{ assets, cursor }
wherecursor
is"Interim_Page"
until we exhaust the list, after which it switches to"Last_Page"
to tell the SDK there’s nothing more to fetch.
- It first ensures the cache is loaded (
- Generous
console.log
statements are sprinkled throughout to make it easy to trace the pagination flow and verify that assets are loaded and served as expected.
Copied to your clipboardimport { v4 as uuidv4 } from "uuid";// Read images/images.json and return its raw dataconst fetchImages = async () => {const resp = await fetch("images/images.json");if (!resp.ok) {throw new Error(`Could not load images.json - ${resp.status} ${resp.statusText}`);}return resp.json(); // [ {file, prompt}, …]};// Convert a single file in /images to a data-URLconst fileToBase64 = async (fileName) => {const resp = await fetch(`images/${fileName}`);const blob = await resp.blob();return new Promise((resolve, reject) => {const reader = new FileReader();reader.onloadend = () => resolve(reader.result); // "data:image/jpeg;base64,…"reader.onerror = reject;reader.readAsDataURL(blob);});};// ------------------------------------------------------------------// one-time cache (so we only hit the network once)// ------------------------------------------------------------------let _cache = null; // [{ prompt, base64 }, …]let _cursor = 0; // how far we have already servedconst PAGE_SIZE = 12; // Firefly’s community wall defaultconst hydrateCache = async () => {if (_cache) return _cache; // already loadedconst meta = await fetchImages(); // from images.jsonconst base64List = await Promise.all(meta.map((item) => fileToBase64(item.file)));_cache = base64List.map((b64, idx) => ({prompt: meta[idx].prompt,base64: b64,}));return _cache;};/*** The very first call returns items 0-11, cursor = 'Interim_Page'* The second call returns items 12-23, etc.* When the last chunk is served, cursor = 'Last_Page'* @returns {Promise<{assets: Array, cursor: 'Interim_Page'|'Last_Page'}>}*/export const fetchCommunityAssets = async () => {console.log("fetchCommunityAssets called!");// Implement your logic here to fetch the assets from the databaseconst data = await hydrateCache();console.log("data", data);// If we have already served all available assets (cursor at or past end),// start over so that a fresh Embed SDK session can see the community wall.if (_cursor >= data.length) { _cursor = 0 }console.log("cursor", _cursor);// slice next PAGE_SIZE elementsconst slice = data.slice(_cursor, _cursor + PAGE_SIZE);const assets = slice.map((item) => ({assetId: uuidv4(),title: item.prompt,thumbnailSrc: item.base64, // 👈 for simplicity, we're using thefullRenditionSrc: item.base64, // same image for both high/low reswidth: 467, // 👈 hard-coded, or swap withheight: 600, // real metadata if you like}));_cursor += slice.length;const cursor = _cursor >= data.length ? "Last_Page" : "Interim_Page";return { assets, cursor };};
The last thing we need to do is to import the fetchCommunityAssets
function in our main.js
file.
Copied to your clipboard// main.jsimport { fetchCommunityAssets } from "./community-wall.js";//... rest of the code ...const appConfig = {appVersion: "2",featureConfig: { "community-wall": true },communityWallConfig: { fetchCommunityAssets }, // 👈 set the function//... rest of the appConfig ...};//... rest of the main.js ...
Troubleshooting
Common issues
Issue | Solution |
---|---|
Error: "Adobe Express is not available" | Check to have entered the correct API Key in the src/.env file as described here. |
The Community Wall is not showing | Make sure you've set the fetchCommunityAssets function in the appConfig |
Complete working example
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>Embed SDK Sample</title></head><body><sp-theme scale="medium" color="light" system="express"><div class="container"><header><h1>Adobe Express Embed SDK</h1><sp-divider size="l"></sp-divider><h2>Generate Image Sample</h2><p>The <b>Generate Image</b> button generates an image from the current project.</p></header><main><img id="savedImage" src="https://placehold.co/300x300?text=Placeholder+Image&font=source-sans-pro"alt="Your design will appear here." /><sp-button-group><sp-button id="generateBtn">Generate Image</sp-button></sp-button-group></main></div></sp-theme><script type="module" src="./main.js"></script></body></html>
Copied to your clipboardimport "./style.css";// Importing theme and typography styles from Spectrum Web Componentsimport "@spectrum-web-components/styles/typography.css";import "@spectrum-web-components/theme/express/theme-light.js";import "@spectrum-web-components/theme/express/scale-medium.js";import "@spectrum-web-components/theme/sp-theme.js";// Importing Spectrum Web Componentsimport "@spectrum-web-components/button/sp-button.js";import "@spectrum-web-components/button-group/sp-button-group.js";import "@spectrum-web-components/divider/sp-divider.js";import { fetchCommunityAssets } from "./community-wall.js";// Importing the Adobe Express Embed SDKawait import("https://cc-embed.adobe.com/sdk/v4/CCEverywhere.js");console.log("CCEverywhere loaded", window.CCEverywhere);// Parameters for initializing the Adobe Express Embed SDKconst hostInfo = {clientId: import.meta.env.VITE_API_KEY,appName: "Embed SDK Sample",};// Prompts the user to login only when exporting/saving the documentconst configParams = {loginMode: "delayed",};// Initializing the Adobe Express Embed SDKconst { module } = await window.CCEverywhere.initialize(hostInfo, configParams);// Will hold the project ID when a document is saved on Adobe Expressvar existingProjectId = null;var expressImage = document.getElementById("savedImage");// Callbacks to be used when creating or editing a documentconst callbacks = {onCancel: () => {},onPublish: (intent, publishParams) => {existingProjectId = publishParams.projectId;console.log("Project ID", existingProjectId);expressImage.src = publishParams.asset[0].data;console.log("Image data", publishParams.asset[0].data);},onError: (err) => {console.error("Error!", err.toString());},onIntentChange: (oldIntent, newIntent) => {console.log("Intent changed from", oldIntent, "to", newIntent);return {callbacks: {onCancel: () => {},onPublish: (intent, publishParams) => {existingProjectId = publishParams.projectId;console.log("Project ID", existingProjectId);expressImage.src = publishParams.asset[0].data;console.log("Image data", publishParams.asset[0].data);},},exportConfig: [{id: "download",label: "Download",action: { target: "download" },style: { uiType: "button" },},{id: "save-modified-asset",label: "Save image",action: { target: "publish" },style: { uiType: "button" },},],containerConfig: {},};},};// Configuration for the app, shared by both Create and Edit flowsconst appConfig = {appVersion: "2",featureConfig: {"community-wall": true,"fast-mode": false,"custom-models": false,},thumbnailOptions: ["rich-preview", "edit-dropdown"],editDropdownOptions: [{ option: "add-effects" },{ option: "remove-background" },{ option: "apply-adjustment" },{ option: "insert-object" },{ option: "remove-object" },],communityWallConfig: {fetchCommunityAssets,},callbacks,};// Configuration for the export options made available to the user// when creating or editing a documentconst exportConfig = [{id: "download",label: "Download",action: { target: "download" },style: { uiType: "button" },},{id: "save-modified-asset",label: "Save image",action: { target: "publish" },style: { uiType: "button" },},];// Click handler for the Create Design buttondocument.getElementById("generateBtn").onclick = async () => {module.createImageFromText(appConfig, exportConfig);};
Copied to your clipboardimport { v4 as uuidv4 } from "uuid";/** Read images/images.json and return its raw data */const fetchImages = async () => {const resp = await fetch("images/images.json");if (!resp.ok) {throw new Error(`Could not load images.json - ${resp.status} ${resp.statusText}`);}return resp.json(); // [{file, prompt}, …]};/** Convert a single file in /images to a data-URL */const fileToBase64 = async (fileName) => {const resp = await fetch(`images/${fileName}`);const blob = await resp.blob();return new Promise((resolve, reject) => {const reader = new FileReader();reader.onloadend = () => resolve(reader.result); // “data:image/jpeg;base64,…”reader.onerror = reject;reader.readAsDataURL(blob);});};/* ------------------------------------------------------------------* one-time cache (so we only hit the network once)* ------------------------------------------------------------------ */let _cache = null; // [{ prompt, base64 }, …]let _cursor = 0; // how far we have already servedconst PAGE_SIZE = 12; // Firefly’s community wall defaultconst hydrateCache = async () => {if (_cache) return _cache; // already loadedconst meta = await fetchImages(); // images.jsonconst base64List = await Promise.all(meta.map((item) => fileToBase64(item.file)));_cache = base64List.map((b64, idx) => ({prompt: meta[idx].prompt,base64: b64,}));return _cache;};/* ------------------------------------------------------------------* public API* ------------------------------------------------------------------ *//*** Mimics Adobe Express Community-Wall pagination.** @returns {Promise<{assets: Array, cursor: 'Interim_Page'|'Last_Page'}>}** The very first call returns items 0-11, cursor = 'Interim_Page'* The second call returns items 12-23, etc.* When the last chunk is served, cursor = 'Last_Page'*/export const fetchCommunityAssets = async () => {console.log("fetchCommunityAssets called!");// Implement your logic here to fetch the assets from the databaseconst data = await hydrateCache();console.log("data", data);// If we have already served all available assets (cursor at or past end),// start over so that a fresh Embed SDK session can see the community wall.if (_cursor >= data.length) {_cursor = 0;}console.log("cursor", _cursor);// slice next PAGE_SIZE elementsconst slice = data.slice(_cursor, _cursor + PAGE_SIZE);const assets = slice.map((item) => ({assetId: uuidv4(),title: item.prompt,thumbnailSrc: item.base64,fullRenditionSrc: item.base64,width: 467, // hard-coded, or swap for real metadata if you likeheight: 600,}));_cursor += slice.length;const cursor = _cursor >= data.length ? "Last_Page" : "Interim_Page";return { assets, cursor };};
Need help?
Have questions or running into issues? Join our Community Forum to get help and connect with other developers working with the Adobe Express Embed SDK.
Related resources
- API Reference - Complete SDK documentation
- Adobe Express Embed SDK Overview - High-level introduction
- Demo Application - Interactive demo showcasing SDK capabilities
- Sample Applications - Working code examples and tutorials
- Changelog - Latest updates and improvements