Querying content
Apps can query the content in a user's design, similar to how the HTML DOM can be queried. They can then read and update the queried content, which unlocks a range of powerful use-cases.
Content is not the same as an element in a design. However, they are related because elements contain content. An app can operate on content through content querying, but needs design editing to position elements and set element dimensions. Even if design editing can reach content in some instances, content querying operates consistently on the content contained within elements. See Design editing for more information, and the Design Editing API guidelines.
Content types
There are different types of content that may appear in a design, including:
For the time being, richtext is the only content type that's compatible with querying.
In the future, other content types will be supported.
Targets
When an app queries a design, it must specify a target. This determines what part of the design to query. For the time being, the only supported target is the current page. In the future, other targets will be supported.
Reading content
Apps can register a callback for any combination of the supported content types and targets:
import { editContent } from "@canva/design";// Read and edit richtext content on current pageawait editContent({contentType: "richtext",target: "current_page",},async (session) => {console.log(session.contents);});
This callback receives a session object that contains both a sync
method for managing changes and a contents
property containing an array of content items that can be read or modified.
A single design may contain multiple pieces of content. You'll typically want to loop through the individual content items:
import { editContent } from "@canva/design";// Read and edit richtext content on current pageawait editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through content itemsfor (const content of session.contents) {console.log(content);}});
Each content item exposes methods and properties for reading and updating the content. The available methods and properties depend on the type of content.
In the case of richtext content, each item is a richtext range:
import { editContent } from "@canva/design";// Read and edit richtext content on current pageawait editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through content itemsfor (const content of session.contents) {// Each content item is a richtext rangeconst regions = content.readTextRegions();console.log(regions);}});
To learn more, see Richtext ranges.
Updating content
Each content item exposes methods and properties that can be used to update the queried content:
import { editContent } from "@canva/design";await editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through each content itemfor (const content of session.contents) {// Get the richtext content as plaintextconst plaintext = content.readPlaintext();// Format the richtextcontent.formatParagraph({ index: 0, length: plaintext.length },{ fontWeight: "bold" });}// Sync the content so that it's reflected in the designawait session.sync();});
But before a change is reflected in the user's design, the session must be synced:
import { editContent } from "@canva/design";await editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through each content itemfor (const content of session.contents) {// Get the richtext content as plaintextconst plaintext = content.readPlaintext();// Format the richtextcontent.formatParagraph({ index: 0, length: plaintext.length },{ fontWeight: "bold" });}// Sync the content so that it's reflected in the designawait session.sync();});
Syncing a session:
- Commits the changes to the user's design.
- Updates the content items so they contain the modified content.
Be aware that:
- Any unsynced changes will be discarded.
- A synced session will not contain new content items that have been created since the
editContent
method was called. To access new content items, an app must make another call toeditContent
. - When editing content, there's a one-minute timeout to avoid the snapshot of the content from becoming too stale. This timeout is reset when the session is synced, so we recommend syncing the session as often as possible.
Handling conflicts
If queried content is changed after the editContent
method is called but before the session is synced, it can lead to conflicts between the state of the content and the true state of the design.
Canva will attempt to resolve conflicts, but we can't make any guarantees about how conflicts will be resolved. To minimize the risk of unexpected behavior, we recommend syncing the session as often as possible. This will reduce the size and complexity of conflicts, leading to more predictable behavior.
There's no limit to how many times a session can be synced.
Handling deleted content
If a queried content item is deleted after the editContent
method is called, the deleted content will continue to exist within the content array, even after the session is synced.
To identify deleted content, each content item has a deleted
property that is either true
or false
. You can use this property to avoid operating on content that no longer exists in the user's design:
import { editContent } from "@canva/design";// Read and edit richtext content on current pageawait editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through the individual content itemsfor (const content of session.contents) {// Ignore deleted contentif (content.deleted) {continue;}// Get the richtext content as plaintextconst plaintext = content.readPlaintext();// Format the richtextcontent.formatParagraph({ index: 0, length: plaintext.length },{ fontWeight: "bold" });}// Sync the content so that it's reflected in the designawait session.sync();});
Gotchas
- You can't use querying to read or update the position of elements on a page. You can only use querying to read and update the content of a design, not its structure. Use the Design Editing features to reposition design elements.
- In content arrays, the order of the content items is not guaranteed. This means an app shouldn't rely on the order of the items being consistent or predictable.
- You can't call the
editContent
method from within the callback of anothereditContent
method — that is, the calls can't be nested. If you try to do this, an error will be thrown.
Example
import { Button, Rows } from "@canva/app-ui-kit";import { editContent } from "@canva/design";import styles from "styles/components.css";export function App() {async function handleClick() {// Read and edit richtext content on current pageawait editContent({contentType: "richtext",target: "current_page",},async (session) => {// Loop through each content itemfor (const content of session.contents) {// Get the richtext content as plaintextconst plaintext = content.readPlaintext();// Format the richtextcontent.formatParagraph({ index: 0, length: plaintext.length },{ fontWeight: "bold" });}// Sync the content so that it's reflected in the designawait session.sync();});}return (<div className={styles.scrollContainer}><Rows spacing="2u"><Button variant="primary" onClick={handleClick}>Update richtext content</Button></Rows></div>);}