On September 25th, 2024, we released v2 of the Apps SDK. To learn what’s new and how to upgrade, see Migration FAQ and Migration guide.

Design Editing API

How to programmatically read and edit designs.

The Design Editing API enables apps to read and edit the "ingredients" of a design, such as pages and elements. This unlocks a range of powerful capabilities for programmatically creating and manipulating designs.

This page introduces the fundamentals of working with the API, along with some common use-cases, but the best way to understand the full scope of what's possible is to browse the API reference.

Our design guidelines help you create a high-quality app that easily passes app review.

Core concepts

The Design Editing API exposes a single openDesign method:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
console.log(session);
});
TSX

This method is packed with powerful capabilities and some subtle nuances, so before diving into using it, it's important to first understand a few key details about how it works.

Design contexts

The first argument of the openDesign method specifies a design context:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
console.log(session);
});
TSX

This determines what part of the design to read and edit. For the time being, apps can only read the current page of the design. In the future, other contexts will be supported.

Sessions

The second argument of the openDesign method is a callback function that receives a session object. The contents of this object partly depends on the design context.

When the design context is the current page, the session object contains:

  • a snapshot of the current page, including the elements it contains
  • helper functions for performing complex operations (e.g., grouping elements)
  • a sync method for syncing the snapshot with the live design

For example:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
console.log(session.page); // the snapshot of the design
console.log(session.helpers); // the helper functions
console.log(session.sync); // the sync() method
});
TSX

Syncing

While the callback is running, the live design may continue to change. For example, someone collaborating on the design may change it in some way. This means that the snapshot of the design and live design can fall out of sync.

To account for this, the callback receives a sync method:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
// Get the initial count of elements
const initialCount = session.page.elements.count();
console.log(`Initial element count: ${initialCount}`);
// The sync method updates the snapshot with the latest state
await session.sync();
// If someone else added elements, the count might be different
const updatedCount = session.page.elements.count();
console.log(`Updated element count: ${updatedCount}`);
if (updatedCount !== initialCount) {
console.log("The design was modified by another user!");
}
});
TS

The sync method does two things:

  • Updates the live design with any edits that have been made to the snapshot.
  • Updates the snapshot, so that the callback has access to the latest state of the design.

That second point is particularly important, as edits to the snapshot are not automatically reflected in the design. Apps must call the sync method for the edits to be applied:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Create a text element
const textElementState =
session.helpers.elementStateBuilder.createTextElement({
top: 100,
left: 100,
width: 200,
text: {
regions: [
{
text: "This text won't appear until we sync!",
formatting: {
fontSize: 20,
color: "#ff0099",
},
},
],
},
});
// Add the element to the page
session.page.elements.insertAfter(undefined, textElementState);
// At this point, the element exists in the snapshot but *not* in the live design
console.log("Element added to snapshot");
// Call sync to apply the changes to the live design
await session.sync();
console.log("Changes synced - element now visible in the design!");
});
TS

Any edits made to the snapshot that are not synced will be discarded when the callback is disposed.

Conflicts

It's possible for there to be conflicts between the snapshot and the live design. In these cases, Canva will attempt to resolve the conflict. If the conflict can't be resolved, the edit that occurs outside of the app will take precedence.

Lists

Throughout the Design Editing API, various things are exposed as lists, including:

  • The elements on a page
  • The elements in a group
  • The paths of a shape
  • The regions of text in a richtext range

These lists provide methods for operating on the lists (and the items within them), such as filter, indexOf, and forEach. These methods are an essential part of reading and editing a design because they allow apps to make both sweeping changes (e.g., all shapes in a design) and targeted changes (e.g., only text elements with a certain string).

The available methods depend on the thing being operated on. For example, when reading a shape element, the shape's paths are read-only, so mutation methods aren't available.

The list methods are comparable to the native JavaScript methods, but are not identical. Be careful about making assumptions about their signatures and behavior.

Pages

Designs are made up of one or more pages.

Using the Design Editing API, apps can:

  • Read the metadata of pages (e.g., whether or not the page is locked)
  • Read and edit the page's ingredients (e.g., the background of a page)

Page types

All pages have an associated type:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
console.log(session.page.type); // => "absolute" or "unsupported"
});
TS

For the time being, apps can only interact with absolute pages. All other types of pages are unsupported. The most notable consequence of this is that the Design Editing API is not compatible with documents(opens in a new tab or window).

Absolute pages

Absolute pages are pages that have either:

  • fixed dimensions (i.e., a width and a height)
  • unbounded dimensions (i.e., are infinite)

An example of an absolute page with fixed dimensions is a presentation page. This is the most common type of page in Canva. The only example of a page that has unbounded dimensions is a whiteboard(opens in a new tab or window).

The following code sample demonstrates how to check if the current page has fixed or unbounded dimensions:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type === "absolute") {
if (session.page.dimensions) {
console.log("The current page has fixed dimensions.");
} else {
console.log("The current page has unbounded dimensions.");
}
}
});
TS

Unsupported pages

When a page is of an unsupported type, apps cannot read or edit it. Before attempting to interact with a page, always check if the page is unsupported:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type === "unsupported") {
console.log("The current page is not supported.");
return;
}
});
TS

Locked pages

Pages can be locked.

When a page is locked, its metadata and ingredients are read-only. If an app attempts to read or edit a locked page, an error is thrown. Before attempting to edit a page, always check if the page is locked:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.locked) {
console.log("The page is locked, so no edits are allowed.");
return;
}
});
TS

Backgrounds

Backgrounds are base layers that appear behind all other ingredients on a page. The appearance of a background is defined as a fill, which means they can contain colors, images, or videos.

The following code sample demonstrates how to check if a page has a background:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
if (!session.page.background) {
console.log("The page doesn't have a background.");
return;
}
console.log(session.page.background); // => Fill
});
TS

To learn more about fills, including how to set them, see Fills.

Elements

Elements are the visual objects that users can add to their designs, such as shapes, text, and groups. Apps can use the Design Editing API to create, read, update, and delete elements.

All elements share some common properties and behavior, which is what this section covers. To learn more about the unique characteristics of the supported element types, see Element types.

Locked elements

Like pages, elements can be locked. When an element is locked, its properties can be read but not modified. Be sure to check if an element is locked before attempting to modify it:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
const textElement = session.page.elements
.toArray()
.find(
(element): element is DesignEditing.TextElement => element.type === "text"
);
if (textElement.locked) {
console.log("The element is locked and cannot be modified.");
}
});
TS

Creating elements

To create an element, first initialize the state of the element with one of the following helper methods:

  • createEmbedElement
  • createRectElement
  • createShapeElement
  • createTextElement

For example:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Create a shape element (e.g., a circle)
const shapeElementState =
session.helpers.elementStateBuilder.createShapeElement({
top: 100,
left: 350,
width: 100,
height: 100,
viewBox: {
top: 0,
left: 0,
width: 100,
height: 100,
},
paths: [
{
d: "M 50 0 A 50 50 0 1 1 50 100 A 50 50 0 1 1 50 0 Z",
fill: {
colorContainer: {
type: "solid",
color: "#0099ff",
},
},
},
],
});
});
TS

Then add the element to the design with one of the available list methods, such as insertAfter:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Create a shape element (e.g., a circle)
const shapeElementState =
session.helpers.elementStateBuilder.createShapeElement({
top: 100,
left: 350,
width: 100,
height: 100,
viewBox: {
top: 0,
left: 0,
width: 100,
height: 100,
},
paths: [
{
d: "M 50 0 A 50 50 0 1 1 50 100 A 50 50 0 1 1 50 0 Z",
fill: {
colorContainer: {
type: "solid",
color: "#0099ff",
},
},
},
],
});
// Insert after the last element
session.page.elements.insertAfter(undefined, shapeElementState);
// Apply changes
await session.sync();
});
TS

Be sure to call the sync method for the change to be reflected in the user's design.

Reading elements

To read the elements on the page, use one of the available list methods, such as forEach or filter:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
// Iterate through all elements
session.page.elements.forEach((element, index) => {
console.log(`Element ${index}: ${element.type}`);
// Read element properties
console.log(`Position: ${element.top}, ${element.left}`);
console.log(`Dimensions: ${element.width}x${element.height}`);
console.log(`Rotation: ${element.rotation}`);
console.log(`Locked: ${element.locked}`);
});
// Filter specific element types
const textElements = session.page.elements.filter(
(element): element is DesignEditing.TextElement => element.type === "text"
);
console.log(`Found ${textElements.length} text elements`);
textElements.forEach((textElement) => {
const plaintext = textElement.text.readPlaintext();
console.log(`Text content: ${plaintext}`);
});
// Find shapes with specific properties
const blueShapes = session.page.elements.filter((element) => {
if (element.type !== "shape") return false;
return element.paths.toArray().some((path) => {
const colorFill = path.fill.colorContainer?.ref;
return colorFill?.type === "solid" && colorFill.color === "#0099ff";
});
});
console.log(`Found ${blueShapes.length} blue shapes`);
});
TS

Updating elements

To update elements, mutate their properties and sync the changes:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
session.page.elements.forEach((element) => {
// Skip locked elements
if (element.locked) {
return;
}
// Update position
element.top += 50;
element.left += 50;
// Update rotation
element.rotation += 15;
// Update transparency
element.transparency = 0.8;
// Update element-specific properties
switch (element.type) {
case "rect":
// Update rect fill color
element.fill.colorContainer.set({
type: "solid",
color: "#ff6600",
});
break;
case "text":
// Update text content and formatting
element.text.replaceText(
{ index: 0, length: element.text.readPlaintext().length },
"Updated text content",
{
color: "#0066ff",
fontSize: 20,
fontWeight: "bold",
}
);
break;
case "shape":
// Update shape path colors
element.paths.forEach((path) => {
if (path.fill.colorContainer?.ref?.type !== "solid") {
return;
}
path.fill.colorContainer.set({
type: "solid",
color: "#00ff66",
});
});
break;
case "embed":
// Embeds have limited update capabilities
console.log(`Embed URL: ${element.url}`);
break;
}
});
// Apply all changes
await session.sync();
});
TS

Be aware that some properties are read-only.

Deleting elements

To delete elements, call the delete method on an element and sync the changes:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Delete a specific element by reference
const elements = session.page.elements.toArray();
if (elements.length > 0) {
// Delete the first element
session.page.elements.delete(elements[0]);
}
// Delete all text elements
const textElements = session.page.elements.filter(
(element): element is DesignEditing.TextElement => element.type === "text"
);
textElements.forEach((textElement) => {
session.page.elements.delete(textElement);
});
// Delete elements based on a condition
session.page.elements.forEach((element) => {
// Delete small elements (less than 50x50 pixels)
if (element.width < 50 && element.height < 50) {
session.page.elements.delete(element);
return;
}
// Delete transparent elements
if (element.transparency && element.transparency > 0.9) {
session.page.elements.delete(element);
return;
}
});
// Delete all elements except groups
const nonGroupElements = session.page.elements.filter(
(element) => element.type !== "group"
);
nonGroupElements.forEach((element) => {
session.page.elements.delete(element);
});
// Apply deletions
await session.sync();
});
TS

Element types

Canva supports a wide variety of elements, a subset of which apps can interact with via the Design Editing API.

The supported element types include:

  • Embeds
  • Groups
  • Rects
  • Shapes
  • Text

Be aware that, unlike other parts of the SDK:

  • The Design Editing API doesn't have a concept of image or video elements. Instead, images are videos are handled as rects with fills. This is more aligned with how Canva works under the hood.
  • Text elements are only available as richtext ranges, rather than richtext and plaintext. However, richtext ranges can be converted into plaintext, so it's only one extra step.
  • Table elements are not currently supported.

This section covers the unique aspects of the supported element types.

Embed elements

Embed elements allow you to include rich media content from external sources, such as YouTube videos, Vimeo content, or other embeddable media supported by Iframely(opens in a new tab or window).

Creating embed elements

To create an embed element, provide a URL to embeddable content using the createEmbedElement helper method:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const embedElementState =
session.helpers.elementStateBuilder.createEmbedElement({
top: 50,
left: 50,
width: 560,
height: 315,
rotation: 0,
transparency: 0,
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
});
session.page.elements.insertAfter(undefined, embedElementState);
await session.sync();
});
TS

Group elements

Group elements contain other elements and allow them to be moved, rotated, and scaled as a single unit. Groups are essential for organizing complex designs.

Grouping elements

To create a group, first add the elements you want to group to the page, then use the group helper method:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Create elements to group
const rectElementState =
session.helpers.elementStateBuilder.createRectElement({
top: 0,
left: 0,
width: 100,
height: 100,
fill: {
colorContainer: {
type: "solid",
color: "#3498db",
},
},
});
const textElementState =
session.helpers.elementStateBuilder.createTextElement({
top: 35,
left: 25,
width: 50,
text: {
regions: [
{
text: "Box",
formatting: {
fontSize: 16,
color: "#ffffff",
textAlign: "center",
},
},
],
},
});
// Add elements to snapshot
const addedRect = session.page.elements.insertAfter(
undefined,
rectElementState
);
const addedText = session.page.elements.insertAfter(
addedRect,
textElementState
);
// Group the elements
const groupElement = await session.helpers.group({
elements: [addedRect, addedText],
});
await session.sync();
});
TS

Ungrouping elements

To ungroup elements, use the ungroup helper method on an existing group:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Find first unlocked group
const [groupElement] = session.page.elements.filter(
(element): element is DesignEditing.GroupElement =>
element.type === "group" && !element.locked
);
if (!groupElement) {
console.log("Group element not found in design.");
return;
}
const ungroupedElements = await session.helpers.ungroup({
element: groupElement,
});
console.log(`Ungrouped ${ungroupedElements.length} elements`);
await session.sync();
});
TS

Iterating through group contents

Groups provide a contents list that allows you to access and iterate through the child elements:

import { openDesign, type DesignEditing } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
const groups = session.page.elements.filter(
(element): element is DesignEditing.GroupElement => element.type === "group"
);
groups.forEach((group) => {
// List all element types in the group
const elementTypes = new Set<string>();
group.contents.forEach((child) => {
elementTypes.add(child.type);
// Child positions are relative to the group
console.log(
`${child.type} at relative position: ${child.top}, ${child.left}`
);
});
console.log(`Group contains: ${Array.from(elementTypes).join(", ")}`);
});
});
TS

Rect elements

Rect elements are rectangular shapes that can be filled with colors, images, or videos.

Creating rects with color fills

To create a rect with a solid color fill, use the colorContainer property:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const rectElementState =
session.helpers.elementStateBuilder.createRectElement({
top: 50,
left: 50,
width: 200,
height: 150,
rotation: 0,
transparency: 0,
fill: {
colorContainer: {
type: "solid",
color: "#ff6b6b",
},
},
});
session.page.elements.insertAfter(undefined, rectElementState);
await session.sync();
});
TS

Creating rects with image fills

To create a rect with an image fill, upload an image asset before setting that asset as the fill:

import { upload } from "@canva/asset";
import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const uploadResult = await upload({
type: "image",
url: "https://example.com/my-image.jpg",
mimeType: "image/jpeg",
thumbnailUrl: "https://example.com/my-image-thumbnail.jpg",
aiDisclosure: "none",
});
const rectElementState =
session.helpers.elementStateBuilder.createRectElement({
top: 50,
left: 50,
width: 300,
height: 200,
fill: {
mediaContainer: {
type: "image",
imageRef: uploadResult.ref,
flipX: false,
flipY: false,
},
},
});
session.page.elements.insertAfter(undefined, rectElementState);
await session.sync();
});
TS

Creating rects with video fills

To create a rect with a video fill, upload a video asset before setting that asset as the fill:

import { upload } from "@canva/asset";
import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const uploadResult = await upload({
type: "video",
url: "https://example.com/my-video.mp4",
mimeType: "video/mp4",
thumbnailImageUrl: "https://example.com/video-thumbnail.jpg",
thumbnailVideoUrl: "https://example.com/video-preview.mp4",
aiDisclosure: "none",
});
const rectElementState =
session.helpers.elementStateBuilder.createRectElement({
top: 50,
left: 50,
width: 300,
height: 200,
fill: {
mediaContainer: {
type: "video",
videoRef: uploadResult.ref,
flipX: false,
flipY: false,
},
},
});
session.page.elements.insertAfter(undefined, rectElementState);
await session.sync();
});
TS

Shape elements

Shape elements are vector graphics defined by SVG-like paths. They support multiple paths with individual fills and strokes, enabling complex vector artwork.

Creating shape elements

To create a shape element, define one or more paths using SVG path syntax:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const shapeElementState =
session.helpers.elementStateBuilder.createShapeElement({
top: 100,
left: 100,
width: 100,
height: 100,
viewBox: {
top: 0,
left: 0,
width: 100,
height: 100,
},
paths: [
{
d: "M 50 0 A 50 50 0 1 1 50 100 A 50 50 0 1 1 50 0 Z",
fill: {
colorContainer: {
type: "solid",
color: "#3498db",
},
},
},
],
});
session.page.elements.insertAfter(undefined, shapeElementState);
await session.sync();
});
TS

Text elements

Text elements render formatted text content using richtext ranges, which support various formatting options and paragraph-level styling.

Creating text elements

To create a text element, define one or more text regions with content and formatting:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
const textElementState =
session.helpers.elementStateBuilder.createTextElement({
top: 50,
left: 50,
width: 300,
rotation: 0,
transparency: 0,
text: {
regions: [
{
text: "Hello, Canva!",
formatting: {
fontSize: 32,
color: "#2c3e50",
fontWeight: "bold",
textAlign: "center",
},
},
],
},
});
session.page.elements.insertAfter(undefined, textElementState);
await session.sync();
});
TS

Fills

A fill defines the interior appearance of a design ingredient. For example, apps can set the background of a page by setting its background to an image fill.

Ingredient types

The following types of ingredients can have fills:

  • Backgrounds
  • Rects
  • Shapes

The way that fills are read and edited is consistent across each of these different types.

Fill types

The following types of fills are supported:

  • Colors
  • Images
  • Videos

In the future, gradient fills will also be supported.

Reading fills

To check if an element has a color fill and read its value:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
// Read page background color
if (session.page.background?.colorContainer?.ref?.type === "solid") {
const color = session.page.background.colorContainer.ref.color;
console.log(`Background color: ${color}`);
}
// Read rect element colors
session.page.elements.forEach((element) => {
if (element.type !== "rect") {
return;
}
const colorFill = element.fill.colorContainer?.ref;
if (colorFill?.type === "solid") {
console.log(`Rect color: ${colorFill.color}`);
}
});
// Read shape path colors
session.page.elements.forEach((element) => {
if (element.type !== "shape") {
return;
}
element.paths.forEach((path, index) => {
const colorFill = path.fill.colorContainer?.ref;
if (colorFill?.type === "solid") {
console.log(`Path ${index} color: ${colorFill.color}`);
}
});
});
});
TS

To check for image fills and access their properties:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
// Read page background image
const bgMedia = session.page.background?.mediaContainer?.ref;
if (bgMedia?.type === "image") {
console.log(`Background image ref: ${bgMedia.imageRef}`);
}
// Read rect element images
session.page.elements.forEach((element) => {
if (element.type !== "rect") {
return;
}
const media = element.fill.mediaContainer?.ref;
if (media?.type === "image") {
console.log(`Rect has image fill: ${media.imageRef}`);
}
});
});
TS

To check for video fills and access their properties:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute") {
return;
}
// Read page background video
const bgMedia = session.page.background?.mediaContainer?.ref;
if (bgMedia?.type === "video") {
console.log(`Background video ref: ${bgMedia.videoRef}`);
}
// Read shape path videos
session.page.elements.forEach((element) => {
if (element.type !== "shape") {
return;
}
element.paths.forEach((path, index) => {
const media = path.fill.mediaContainer?.ref;
if (media?.type === "video") {
console.log(`Path ${index} has video fill: ${media.videoRef}`);
}
});
});
});
TS

Setting fills

Color fills are the simplest type of fill, consisting of a solid color specified as a hex code:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Set page background to a solid color
if (session.page.background) {
session.page.background.colorContainer.set({
type: "solid",
color: "#f0f0f0",
});
}
// Set rect element fills to colors
session.page.elements.forEach((element) => {
if (element.type !== "rect" || element.locked) {
return;
}
element.fill.colorContainer.set({
type: "solid",
color: "#ff00ff",
});
});
// Set different colors for shape paths
session.page.elements.forEach((element) => {
if (element.type !== "shape" || element.locked) {
return;
}
element.paths.forEach((path, index) => {
const colors = ["#ff0000", "#00ff00", "#0000ff"];
path.fill.colorContainer.set({
type: "solid",
color: colors[index % colors.length],
});
});
});
await session.sync();
});
TS

Image fills require uploading an image asset first, then using the image reference:

import { upload } from "@canva/asset";
import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Upload an image asset
const imageAsset = await upload({
type: "image",
url: "https://example.com/background.jpg",
mimeType: "image/jpeg",
thumbnailUrl: "https://example.com/background-thumb.jpg",
aiDisclosure: "none",
});
// Set page background to an image
if (session.page.background) {
session.page.background.mediaContainer.set({
type: "image",
imageRef: imageAsset.ref,
flipX: false,
flipY: false,
});
}
// Set rect fills to images
session.page.elements.forEach((element) => {
if (element.type !== "rect" || element.locked) {
return;
}
element.fill.mediaContainer.set({
type: "image",
imageRef: imageAsset.ref,
flipX: true, // Flip horizontally
flipY: false,
});
});
await session.sync();
});
TS

Video fills work similarly to image fills but use video assets:

import { upload } from "@canva/asset";
import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Upload a video asset
const videoAsset = await upload({
type: "video",
url: "https://example.com/background-video.mp4",
mimeType: "video/mp4",
thumbnailImageUrl: "https://example.com/video-thumb.jpg",
aiDisclosure: "none",
});
// Set page background to a video
if (session.page.background) {
session.page.background.mediaContainer.set({
type: "video",
videoRef: videoAsset.ref,
flipX: false,
flipY: false,
});
}
// Set shape fills to videos (if editable)
session.page.elements.forEach((element) => {
if (element.type !== "shape" || element.locked) {
return;
}
element.paths.forEach((path) => {
if (!path.fill.isMediaEditable) {
return;
}
path.fill.mediaContainer.set({
type: "video",
videoRef: videoAsset.ref,
flipX: false,
flipY: false,
});
});
});
await session.sync();
});
TS

Clearing fills

To remove fills and make elements transparent, set the fill to undefined:

import { openDesign } from "@canva/design";
await openDesign({ type: "current_page" }, async (session) => {
if (session.page.type !== "absolute" || session.page.locked) {
return;
}
// Clear all fills from rect elements
session.page.elements.forEach((element) => {
if (element.type !== "rect" || element.locked) {
return;
}
// Remove both color and media fills
element.fill.colorContainer.set(undefined);
element.fill.mediaContainer.set(undefined);
});
// Clear only media fill from page background (keep color)
if (session.page.background) {
session.page.background.mediaContainer.set(undefined);
}
await session.sync();
});
TS