Design Editor intent upgrade guide

How to upgrade to the Design Editor intent pattern.

This guide explains all of the code changes required to upgrade from the legacy direct render pattern to the new Design Editor intent pattern. The Design Editor intent provides a standardized way to render apps within the Canva editor and ensures better compatibility with future Canva features.

Automated migration tool

You can use the Canva CLI automated migration tool to handle most of the required changes. The CLI automatically updates:

  • Package installation for @canva/intents
  • Import statements to include prepareDesignEditor
  • Render function to use async/await
  • Code structure to wrap render logic with prepareDesignEditor()
  • App manifest configuration in Developer Portal

For detailed CLI usage, see the Canva CLI documentation.

Run the migration tool

  1. If you haven't already, install the Canva CLI:

    npm install -g @canva/cli@latest
    SHELL
  2. In your app directory, run the migration command:

    canva apps migrate design-editor-intent
    SHELL

After running the automated migration, review the changes and complete any manual steps outlined in the following sections.

Manual migration steps

The following sections explain the specific code changes if you prefer to upgrade manually or need to handle cases not covered by the automated tool.

Install the @canva/intents package

The Design Editor intent requires the @canva/intents package. Install it by running:

npm install @canva/intents@latest
SHELL

Update your app's index file

The main code change involves updating how your app initializes and renders its UI. This typically happens in your src/index.tsx file.

Before: Direct render pattern

In the legacy pattern, apps directly called the render function:

import { createRoot } from "react-dom/client";
import { App } from "./app";
const root = createRoot(document.getElementById("root"));
function render() {
root.render(<App />);
}
render(); // Direct function call
TSX

After: Design Editor intent pattern

With the Design Editor intent, you wrap your render logic with prepareDesignEditor():

import { createRoot } from "react-dom/client";
import { App } from "./app";
import { prepareDesignEditor } from "@canva/intents/design";
const root = createRoot(document.getElementById("root"));
async function render() {
root.render(<App />);
}
prepareDesignEditor({ render }); // Wrap render function
TSX

The key changes are:

  1. Import prepareDesignEditor from @canva/intents/design
  2. Make render function async by adding the async keyword
  3. Replace direct render call with prepareDesignEditor({ render })

Enable the Design Editor intent

After updating your code, you need to enable the Design Editor intent in your app's configuration.

You can configure intents for your app with either the Developer Portal or the Canva CLI:

To enable the Design Editor intent:

  1. Navigate to an app on the Your apps(opens in a new tab or window) page.
  2. On the Compatibility page, find the Intents section.
  3. Enable the Design Editor switch.
  1. Set up your app to use the Canva CLI to manage settings using the canva-app.json file.

  2. Set the intent.design_editor property to be enrolled. For more information, see canva-app.json.

    {
    "intent": {
    "design_editor": {
    "enrolled": true
    }
    }
    }
    JSON
  3. Push the configuration to the Developer Portal:

    canva apps push
    SHELL

Advanced: Custom render implementation

If your app has a more complex rendering setup, you may need to adapt the pattern accordingly. The render function passed to prepareDesignEditor() can contain any initialization logic your app needs:

import { createRoot } from "react-dom/client";
import { AppUiProvider } from "@canva/app-ui-kit";
import { prepareDesignEditor } from "@canva/intents/design";
import { App } from "./app";
import "@canva/app-ui-kit/styles.css";
prepareDesignEditor({
render: async () => {
// Find the root element
const rootElement = document.getElementById("root");
if (!rootElement) {
throw new Error("Unable to find element with id of 'root'");
}
// Create React root
const root = createRoot(rootElement);
// Render your app with any providers
root.render(
<AppUiProvider>
<App />
</AppUiProvider>
);
},
});
TSX

Update tests

If your app has tests that verify the rendering behavior, you'll need to update them to account for the new pattern:

Before:

import { render } from "./index";
render(); // Called directly in tests
TSX

After:

import { prepareDesignEditor } from "@canva/intents/design";
// Tests should mock prepareDesignEditor or test the render function passed to it

Verify the migration

After completing the migration:

  1. Build your app to check for TypeScript errors:

    npm run build
    SHELL
  2. Preview your app in the Canva editor:

    canva apps preview
    SHELL
  3. Test functionality by opening your app in Canva and verifying it loads correctly.