Search
Loading...
Skip to content

Create a Collage

This guide shows you how to add a Collage feature in your web app with the help of the CE.SDK. A collage allows you to reuse images and assets as you change the layout of the scene.

Example of a layout change in the demo app
15 mins
estimated time
Download
StackBlitz
GitHub

Layouts in CE.SDK are predefined templates that dictate how to arrange images and assets within a single composition. They allow you to create visually appealing collages by specifying each image’s:

  • Position
  • Size
  • Orientation relative to other assets on the page

Layouts instantly create visuals and allow you to skip manually arranging each asset.

What You’ll Learn#

In this guide, you will learn how to:

  • Set up the layout panel in the CE.SDK UI.
  • Use a layout file for collages.
  • Use TypeScript to apply layouts in your custom UI.

When to Use Layouts#

The CE.SDK Layout feature is ideal for:

  • Photo collages
  • Grid layouts
  • Magazine spreads
  • Social media posts

Difference Between Layouts and Templates#

Layouts are custom assets that differ from templates:

  • Templates: Load new assets and replace the existing scene.
  • Layouts: Keep existing assets but change their arrangement on the page.

Preserving assets while changing the layout isn’t a native CE.SDK feature. The following sections explain how to leverage the CE.SDK to do it in your app using JavaScript.

How Collages Work#

When you choose a collage layout, the app needs to:

  1. Load a new layout file.
  2. Extract content from your current design.
  3. Map content to new layout positions.
  4. Preserve images and text in visual order.

You trigger this workflow when a user selects a layout from the Layouts panel in the CE.SDK UI.

Add a Layouts Panel to the CE.SDK UI#

The CE.SDK allows you to add custom panels to the UI . To add a Layouts panel for collage selection, you need to create it and load custom assets to configure its appearance and behavior.

For this action, you need:

  1. A layout file defining the collage structure (use this one to get started).
  2. Thumbnail images for preview (find a collection here).

1. Add a Layouts Option to the CE.SDK Menu#

To customize the CE.SDK UI and create a Layouts panel, use the CE.SDK UserInterfaceAPI. Add a Layout button with ui.setDockOrder(), using the following properties:

  • id
  • key
  • label
  • icon
  • entries

For example:

cesdk.ui.setDockOrder([
{
id: 'ly.img.assetLibrary.dock',
key: 'layouts-dock-entry',
label: 'Layouts',
icon: '@imgly/Layout',
entries: ['layouts'],
},
]);
Default dock vs. custom dock with layout option.

2. Customize the Layouts Panel Appearance#

To configure how the Layouts panel looks and behaves, use the ui.addAssetLibraryEntry() helper with the following options:

  • id: Unique identifier for the panel.
  • sourceIds: Which assets to display.
  • previewLength: How many preview items to display in the panel.
  • gridColumns: How many columns to contain the previews in the panel.
  • gridItemHeight: The shape of each tile in the panel grid.
  • previewBackgroundType: How to fit the previews inside their tiles (cover/contain).
  • gridBackgroundType: How to display the panel background (cover/contain).

For example, the demo uses this configuration:

CustomCase.jsx
instance.ui.addAssetLibraryEntry({
id: 'ly.img.layouts', // Referenced in the dock entry in Step 1
sourceIds: ['ly.img.layouts'], // Points to our custom layout asset source
previewLength: 2, // Number of preview items int the compact panel
gridColumns: 2, // Organize tiles in 2 columns
gridItemHeight: 'square', // Square tiles
previewBackgroundType: 'contain', // Fit compact panel background
gridBackgroundType: 'contain' // Fit panel background
});
Default dock vs. custom dock with layout option.

To learn more about panel customization, check the Panel Customization guide .

3. Load Custom Assets#

The demo uses a helper function to load a custom layout asset source from a JSON file. This file defines the available layouts and their metadata. You can reuse this logic by defining both:

  • A ContentJSON object (like CustomLayouts.json)
  • A baseURL for your custom assets that replaces the {{base_url}}.
CustomCase.jsx
import { createApplyLayoutAsset } from './lib/createApplyLayoutAsset';
import loadAssetSourceFromContentJSON from './lib/loadAssetSourceFromContentJSON';
// ...
const caseAssetPath = (path, caseId = 'layouts') =>
`${process.env.NEXT_PUBLIC_URL_HOSTNAME}${process.env.NEXT_PUBLIC_URL}/cases/${caseId}${path}`;
// Call the helper to load the layout assets
loadAssetSourceFromContentJSON(
instance.engine, // Pss the CE.SDK engine to load assets into
LAYOUT_ASSETS, // Pass the JSON bundle
caseAssetPath(''), // Base URL for assets
createApplyLayoutAsset(instance.engine) // Callback to createApplyLayoutAsset.js helper
);
await instance.loadFromURL(caseAssetPath('/custom-layouts.scene')); // Load the scene
// ...

In the previous example, the helper accepts an optional applyAsset callback, so:

  1. A user picks a layout from the library.
  2. The engine invokes the callback to apply it.
  3. The engine replaces the current page’s structure with the layout while keeping the user’s images/text.

Asset preservation isn’t a CE.SDK native feature. It’s handled in the createApplyLayoutAsset.js helper, which you can find in the repository.

Apply the Collage#

When applying a collage, the following actions need to be implemented:

  1. Changing the structure of the design.
  2. Transferring the existing content to the new structure.
  3. Deleting the previous scene.

You can find this workflow in the createApplyLayoutAsset() helper from the demo. Follow these steps to replicate it:

1. Prepare and Allow changes#

  • Allow block deletion with editor.setGlobalScope('lifecycle/destroy', 'Allow').
  • Clear the selected blocks with block.setSelected(block, false).
createApplyLayoutAsset.js
const scopeBefore = engine.editor.getGlobalScope('lifecycle/destroy');
engine.editor.setGlobalScope('lifecycle/destroy', 'Allow');
const page = engine.scene.getCurrentPage();
engine.block.findAllSelected().forEach((block) => engine.block.setSelected(block, false));

2. Load the New Layout#

  • Load the new layout with block.loadFromString().
  • Return the first page from the loaded blocks as the layout page.
createApplyLayoutAsset.js
const sceneString = await fetch(asset.meta.uri).then((response) => response.text());
const blocks = await engine.block.loadFromString(sceneString);
const layoutPage = blocks[0];

3. Backup Current Page#

  • Duplicate the current page with block.duplicate() to keep a backup of the existing content.
  • The CE.SDK uses this backup to transfer images and text to the new layout.
  • Clear the current page structure by destroying all its children.
createApplyLayoutAsset.js
const oldPage = engine.block.duplicate(page);
engine.block.getChildren(page).forEach((child) => {
engine.block.destroy(child);
});
engine.block.getChildren(layoutPage).forEach((child) => {
engine.block.insertChild(page, child, engine.block.getChildren(page).length);
});

4. Transfer Content#

Copy user text and images onto the new layout:

createApplyLayoutAsset.js
copyAssets(engine, oldPage, page);

5. Sort Blocks Visually and Pair Content#

Grab text and image blocks from both pages in visual order (top to bottom, left to right):

createApplyLayoutAsset.js
const fromChildren = visuallySortBlocks(engine, getChildrenTree(engine, fromPageId).flat());
const textsOnFromPage = fromChildren.filter((childId) => engine.block.getType(childId).includes('text'));
const imagesOnFromPage = fromChildren.filter((childId) => engine.block.getKind(childId) === 'image');
// same for toPageId -> textsOnToPage, imagesOnToPage

Then apply the content from the old page to the new layout by looping through the blocks:

createApplyLayoutAsset.js
const fromText = engine.block.getString(fromBlock, 'text/text');
const fromFontFileUri = engine.block.getString(fromBlock, 'text/fontFileUri');
const fromTypeface = engine.block.getTypeface(fromBlock);
engine.block.setFont(toBlock, fromFontFileUri, fromTypeface);
const fromTextFillColor = engine.block.getColor(fromBlock, 'fill/solid/color');
engine.block.setString(toBlock, 'text/text', fromText);
engine.block.setColor(toBlock, 'fill/solid/color', fromTextFillColor);

6. Cleanup and Restore State#

Cleanup temporary blocks with block.destroy() and restore global scope:

createApplyLayoutAsset.js
engine.block.destroy(oldPage);
engine.block.destroy(layoutPage);
engine.editor.setGlobalScope('lifecycle/destroy', scopeBefore);
if (config.addUndoStep) {
engine.editor.addUndoStep();
}
return page;

This keeps the editor stable and predictable after the layout changes and prevents:

  • Stray selections.
  • Ghost placeholders.
  • Unused assets left at the scene.

Advanced Collage Techniques#

The CE.SDK BlockAPI eases the transfer of placeholder behavior when moving images between blocks. You can use it to:

  1. Checks if the block supports placeholder behavior with supportsPlaceholderBehavior.
  2. Apply the same setting to the target image block with:
  • isPlaceholderBehaviorEnabled()
  • setPlaceholderBehaviorEnabled()
createApplyLayoutAsset.js
if (engine.block.supportsPlaceholderBehavior(fromBlock)) {
engine.block.setPlaceholderBehaviorEnabled(
toBlock,
engine.block.isPlaceholderBehaviorEnabled(fromBlock),
);
}

This maintains the behavior of current placeholders after the layout swap.

Optimize the Layout Workflow#

For a better user experience when creating collages, consider these optimizations:

TopicStrategies
Asset Loading・ Lazy load thumbnails and cache scene files
・ Preload common layouts and minimize file sizes
・ Use CDN for efficient asset delivery
Content Transfer・ Batch block operations to reduce engine calls
・ Optimize sorting algorithms and memory usage
・ Handle large page structures efficiently
Visual Cues・ Show loading states and support undo/redo
・ Add error recovery and prevent accidental changes

Troubleshooting#

❌ Issue✅ Solutions
Layout not applying・ Verify asset source registration and callback connection
・ Check browser console for scene files or CORS errors
・ Check the validity of scene file URLs
Content lost during layout change・ Debug visual sorting with console logs
・ Verify block type filtering is correct
・ Test with different content settings
Incorrect visual order・ Adjust coordinate rounding tolerance in sorting
・ Flatten complex hierarchies before sorting
・ Test with well-defined layout structures
Performance degradation・ Optimize scene file sizes
・ Batch engine operations
・ Profile and optimize content transfer
Undo not working・ Ensure addUndoStep() called after all changes complete
・ Verify undo configuration in engine setup

Next Steps#

Now that know how to create collages with layouts, explore these related guides to expand your CE.SDK knowledge: