Skip to content

Upgrades and Migrations

Overview

Keep your app up to date with the latest Canva features and best practices.

As the Canva Apps platform evolves, we release new versions of the Apps SDK, App UI Kit, and introduce new patterns to improve app performance, compatibility, and developer experience. This section provides guides to help you upgrade your apps to use the latest features and APIs.

Available migrations

The following migration guides are available to help you upgrade your app:

  • Apps SDK v2: Migrate your app from Apps SDK v1 to v2, which includes breaking changes and new APIs.
  • Design Editor intent: Migrate your app from the legacy direct render pattern to the standardized Design Editor intent pattern.
  • App UI Kit v5: Migrate your app from App UI Kit v4 to v5, which includes a new design token system and component updates.

Automated migrations with the Canva CLI

The Canva CLI includes an automated migration tool that can handle most of the code changes required for these upgrades. The canva apps migrate command analyzes your app's code and automatically updates:

  • Package imports and versions
  • API method signatures
  • Component properties and design tokens
  • App configuration files

Using the migrate command

To use the automated migration tool:

  1. Install the Canva CLI if you haven't already:

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

    shell
    canva apps migrate <migrationName>

Example

To migrate your app to Apps SDK v2:

shell
canva apps migrate apps-sdk-v1-v2

The tool will analyze your code, make the necessary changes, and provide a summary of what was updated. After running the migration, review the changes and test your app to ensure everything works as expected.

NOTE: While the automated migration tool handles most changes, some migrations may require additional manual steps. Always refer to the specific migration guide for complete instructions.

Next steps

  • Review the specific migration guide for your upgrade.
  • Learn more about the Canva CLI.
  • Check the API reference for the latest API documentation.

V2 Migration Guide

How to upgrade to Apps SDK v2.

This guide explains all of the code changes required to upgrade to Apps SDK v2. For general information about upgrades, including deadlines, see the Upgrade FAQ.

Automated migration tool

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

  • Package imports and versions
  • API method signatures
  • Required property additions (such as aiDisclosure)
  • Deprecated method replacements

For detailed CLI usage, see the Canva CLI documentation.

Run the migration tool

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

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

    shell
    canva apps migrate apps-sdk-v1-v2

Note: 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.

Packages

All the npm packages for the Apps SDK have been updated with new TypeScript definitions. These definitions make it easier to identify what parts of your codebase need to be updated.

To install the updates, run the following command:

shell
npm install @canva/asset@latest @canva/design@latest @canva/error@latest @canva/platform@latest @canva/user@latest

Legacy /sdk directory

If you're using an early version of the Apps SDK starter kit that included a local /sdk directory at the root of the starter kit repo, you should:

  1. Remove the local /sdk directory.

  2. Update any imports to use the latest npm packages instead.

    For example, replace the old local imports:

    tsx
    // Old imports from local /sdk directory
    import { initAppElement } from "../sdk/design";
    import { auth } from "../sdk/user";

    with the latest npm package imports:

    tsx
    // Update to npm packages
    import { initAppElement } from "@canva/design";
    import { auth } from "@canva/user";

AI disclosure

In v2, an aiDisclosure property is required when uploading assets. This property indicates whether an asset was generated by AI, and helps maintain transparency about the origin of assets:

tsx
await upload({
  type: "audio",
  title: "Example audio",
  url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",
  mimeType: "audio/mp3",
  durationMs: 86047,
  aiDisclosure: "none", // => Or "app_generated"
});

Alternative text

In v2, creating an element requires an altText property to be set:

tsx
await addElementAtPoint({
  type: "image",
  ref: result.ref,
  altText: {
    text: "Example image",
    decorative: false,
  },
});

If need be, this value can be explicitly set to undefined:

tsx
await addElementAtPoint({
  type: "image",
  ref: result.ref,
  altText: undefined,
});

Authentication

In v1, apps could use manual authentication to authenticate users through third-party platforms:

tsx
import { auth } from "@canva/user";

const response = await auth.requestAuthentication();

switch (response.status) {
  case "COMPLETED":
    console.log("The authentication was successful.");
    break;
  case "ABORTED":
    console.log("The user exited the authentication flow.");
    break;
  case "DENIED":
    console.log("The user didn't have the required permissions.");
    break;
}

In v2, manual authentication has been removed.

Apps have two alternatives for authenticating users:

  • Frictionless auth
  • OAuth

Feature support

Apps can now run in documents, but some APIs aren't compatible with documents.

In v1, there's no workaround for this. If an app uses incompatible APIs, it will be disabled for documents.

In v2, we've introduced the following methods:

  • isSupported
  • registerOnSupportChange

Apps can use these methods to check if a method is available in the current context. This allows the app to adapt to the context in which it's running.

For example, you can place the two add element methods in an array and use Array.find in combination with isSupported to get the appropriate add element method for the current document type. In the following code example, the Add element button will be disabled if neither addElementAtPoint nor addElementAtCursor is supported:

tsx
import { Button, Rows } from "@canva/app-ui-kit";
import { addElementAtCursor, addElementAtPoint } from "@canva/design";
import { useFeatureSupport } from "@canva/app-hooks";
import * as styles from "styles/components.css";

export const App = () => {
  const isSupported = useFeatureSupport();
  const addElement = [addElementAtPoint, addElementAtCursor].find((fn) =>
    isSupported(fn),
  );

  function handleClick() {
    addElement?.({
      type: "text",
      children: ["Hello world"],
    });
  }

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="1u">
        <Button
          variant="primary"
          onClick={handleClick}
          disabled={!addElement}
          tooltipLabel={
            !addElement ? "This feature is not supported in the current page" : undefined
          }
        >
          Add element
        </Button>
      </Rows>
    </div>
  );
};

To learn more, see Feature support.

Adding elements

In v1, elements could be added to a design with the addNativeElement method:

tsx
await addNativeElement({
  type: "TEXT",
  children: ["Hello world"],
});

In v2, this method has been deprecated and superseded by the following methods:

  • addElementAtPoint, which is a renamed version of addNativeElement.
  • addElementAtCursor, which is a new method that's compatible with documents.

These methods are only available in certain contexts and, as a result, apps should use the isSupported method to check that the methods are available in the current context before calling them:

tsx
import { Button, Rows } from "@canva/app-ui-kit";
import { addElementAtCursor, addElementAtPoint } from "@canva/design";
import { useFeatureSupport } from "@canva/app-hooks";
import * as styles from "styles/components.css";

export const App = () => {
  const isSupported = useFeatureSupport();
  const addElement = [addElementAtPoint, addElementAtCursor].find((fn) =>
    isSupported(fn),
  );

  function handleClick() {
    addElement?.({
      type: "text",
      children: ["Hello world"],
    });
  }

  return (
    <div className={styles.scrollContainer}>
      <Rows spacing="1u">
        <Button
          variant="primary"
          onClick={handleClick}
          disabled={!addElement}
          tooltipLabel={!addElement ? "This feature is not supported in the current page" : undefined}
        >
          Add element
        </Button>
      </Rows>
    </div>
  );
};

To learn more, see:

  • Elements
  • Feature support

Asset IDs

In v1, the upload method accepted an id property:

tsx
await upload({
  type: "IMAGE",
  id: "exampleid",
  mimeType: "image/jpeg",
  url: "https://www.canva.dev/example-assets/image-import/image.jpg",
  thumbnailUrl:
    "https://www.canva.dev/example-assets/image-import/thumbnail.jpg",
});

This property has been deprecated for a while and, in v2, it's been removed entirely:

tsx
await upload({
  type: "IMAGE",
  mimeType: "image/jpeg",
  url: "https://www.canva.dev/example-assets/image-import/image.jpg",
  thumbnailUrl:
    "https://www.canva.dev/example-assets/image-import/thumbnail.jpg",
  aiDisclosure: "none",
});

Discriminators

In v1, some discrimintor values were uppercase:

tsx
await addNativeElement({
  type: "EMBED",
  url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
});

In v2, all discriminator values are lowercase:

tsx
await addNativeElement({
  type: "embed",
  url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
});

Drag and drop

In v1, apps could support drag and drop with the startDrag or makeDraggable methods:

<Tabs id="dnd-tab"> <Tab name="startDrag"> tsx ui.startDrag(event, { type: "TEXT", children: ["Add text into a design"], }); </Tab>

<Tab name="makeDraggable"> tsx ui.makeDraggable({ node, dragData: { type: "TEXT", children: ["Hello world"], }, onDragStart: () => { console.log("The drag event has started"); }, onDragEnd: () => { console.log("The drag event has ended"); }, }); </Tab> </Tabs>

In v2, startDrag has been deprecated and makeDraggable has been removed.

Instead, apps should use the following methods:

  • startDragToPoint
  • startDragToCursor

startDragToPoint is identical to startDrag, aside from the name, while startDragToCursor is a new method that's compatible with documents.

Because these methods are only available in certain contexts, apps should use the isSupported method to check that the methods are available in the current context.

To learn more, see:

  • Drag and drop
  • Feature support

Uploading assets

In v1, the queueMediaUpload and whenUploaded methods preceded the upload method. These methods have been deprecated for a while and have now been removed.

Apps should only upload assets with the upload method.

V2 Migration FAQ

Some important things to know about upgrading to Apps SDK v2.

On September 25th, 2024, we launched v2 of the Apps SDK. This version of the SDK includes major, breaking changes that will require all developers to upgrade their apps.

This page answers the most important questions about the new version.

What's changing?

A lot of things!

Some APIs are being deprecated. Some APIs are being introduced.

For the complete list of changes, see the Upgrade guide.

Do I have to upgrade my app?

Yes. All developers must upgrade their app(s) to v2.

How long do I have to upgrade?

You have 12 months, starting from September 25th, 2024. To avoid downtime, the upgraded app should be released by September 25th, 2025.

How do I upgrade my app?

See the Upgrade guide.

How long does it take to upgrade?

This depends on the complexity of the app and the APIs that it uses. That said, we've been careful to minimize the number of required changes. For some developers, it'll only take a matter of minutes to upgrade.

What happens if I don't upgrade?

Your app(s) will be disabled and removed from the Apps Marketplace after September 25th, 2025.

Can I still create v1 apps?

No. All new apps created via the Developer Portal must use v2.

Can I launch my existing v1 app?

Yes, but only until the end of 2024. If the app is not released before the end of 2024, it must be upgraded to v2 before it can be launched.

Can I use a combination of v1 and v2 APIs?

No. The APIs are not interoperable.

Where is the documentation for v2?

All of the existing documentation has been updated to reflect the changes in v2. You can, however, still access the API reference documentation for v1.

App UI Kit V5 Migration

Upgrade your app from App UI Kit v4 to v5.

Version 5 of the App UI Kit includes new design tokens that replace the v4 tokens with a new naming system and improved semantic structure.

This page provides the complete mapping from v4 to v5 design tokens to help you upgrade your app.

For more information about design tokens and when to use them, see the design tokens overview.

There are also some breaking changes in the component library. For the full list of changes, see the changelog .

What's changing?

The App UI Kit v5 introduces a new design token system with:

  • Improved semantic naming: Tokens now have clearer, more descriptive names.
  • Better organization: Tokens are grouped by purpose (action, content, feedback, and so on).
  • Enhanced consistency: More consistent naming patterns across all token types.

There are also some breaking changes to the properties of the Link, Pill, InputPill, Columns, and ColorSelector components.

Finally, we have upgraded from React 18 to 19 so you may have other dependencies that need to change, or breaking changes in React to accommodate. For more information about upgrading to React 19, see the react.dev upgrade guide.

How to automatically migrate

Run the app-ui-kit-v5 Canva CLI apps migrate command in your app's project directory to use the Canva CLI to automatically migrate your app to use App UI Kit v5:

bash
npx @canva/cli@latest apps migrate app-ui-kit-v5

It automatically runs through the code changes required to install the updated dependencies, migrates design tokens, and changes component properties.

How to manually migrate

  1. Update your dependencies: Upgrade to the latest version of @canva/app-ui-kit.
  2. Find and replace tokens: Use the tables below to map your v4 tokens to v5 tokens.
  3. Update components: There are some small breaking changes to component properties in v5.
  4. Test your app: Verify that your app still looks and works correctly.

Migration example

Here's an example of migrating a CSS class from v4 to v5 tokens:

Before (v4 tokens):

css
.my-component {
  background-color: var(--ui-kit-color-primary);
  color: var(--ui-kit-color-typography-primary);
  padding: var(--ui-kit-space-2);
}

After (v5 tokens):

css
.my-component {
  background-color: var(--ui-kit-color-action-primary-bg);
  color: var(--ui-kit-color-content-fg);
  padding: var(--ui-kit-space-200);
}

Do I need to migrate the design tokens?

  • If you only use App UI Kit components: You don't need to do anything. The components will automatically use the new v5 tokens.

  • If you use design tokens directly in your CSS or TypeScript: You'll need to update your code to use the new v5 token names.

Migration mappings

The tables below show the complete mapping from v4 to v5 design tokens.

Color tokens

Shadow and surface tokens

Spacing tokens

Component changes

In addition to design token changes, App UI Kit v5 includes several component updates:

React version requirement

  • Breaking: React 19 is now required and React 18 support was removed.

    Migration: Update your project to use React v19.

  • Breaking: Removed deprecated title prop.

    Migration: Use tooltipLabel instead of title.

tsx
// Before (v4)
<Link title="Click here" href="/example">Link text</Link>

// After (v5)
<Link tooltipLabel="Click here" href="/example">Link text</Link>

ColorSelector component

  • Breaking: The source parameter in onChange callback is now optional.

    Migration: Check for undefined values in your callback handler.

tsx
// Before (v4)
<ColorSelector onChange={(color, source) => {
  // source was always defined
  console.log(source);
}} />

// After (v5)
<ColorSelector onChange={(color, source) => {
  // source might be undefined
  if (source) {
    console.log(source);
  }
}} />

Column component

  • Breaking: Default alignY property changed from implicit to stretch.

    Migration: To use the old behavior, explicitly set alignY="start".

tsx
// Before (v4) - implicit start alignment
<Column>Content</Column>

// After (v5) - explicit start alignment needed
<Column alignY="start">Content</Column>

Pill and InputPill components

  • Breaking: Replaced truncateText prop with maxWidth prop.
  • Breaking: Changed size values: "small" is now "xsmall".
  • Breaking: Pill has a rounder appearance for consistency with other Canva elements.
  • Breaking: Removed tone property.
tsx
// Before (v4)
<Pill size="small" truncateText>Text</Pill>
<InputPill truncateText={false}>Text</InputPill>

// After (v5)
<Pill size="xsmall" maxWidth='25u'>Text</Pill>
<InputPill maxWidth='100%'>Text</InputPill>

Global styling

  • Breaking: Removed the default colorSurface background color from the html tag.

    A migration isn't be required, the Canva environment has changed to accommodate this.

New components

  • Added: LinkButton component for in-app actions that don't open URLs.

    Use LinkButton instead of Button when you need link-style interactions without navigation

Design Editor Migration

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 change to wrap render logic in an object to implement the DesignEditorIntent type.
  • Code structure changes to match the recommended practices for intents by creating src/intents/design_editor/index.tsx.
  • Code change to call prepareDesignEditor() in the src/index.tsx file which imports the implementation of the DesignEditorIntent type.
  • 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:

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

    shell
    canva apps migrate design-editor-intent

Note: 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:

shell
npm install @canva/intents@latest

Update your app's index file

The main code change involves updating how your app initializes and renders its UI. We recommend this happens in a new src/intents/design_editor/index.tsx file, which is imported from src/index.tsx.

Before: Direct render pattern

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

tsx
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

After: Design Editor intent pattern

With the Design Editor intent, you implement the intent contract in src/intents/design_editor/index.tsx and call prepareDesignEditor() in src/index.tsx:

tsx
import type { DesignEditorIntent } from "@canva/intents/design";
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 />);
}

const designEditor: DesignEditorIntent = { render };
export default designEditor; // default export the intent contract

This new file should be imported from src/index.tsx:

tsx
import { prepareDesignEditor } from "@canva/intents/design";

import designEditor from "./intents/design_editor";

prepareDesignEditor(designEditor);

The key changes are:

  1. Import prepareDesignEditor from @canva/intents/design in src/index.tsx
  2. Export the designEditor implementation object from src/intents/design_editor/index.tsx
  3. Make render function async by adding the async keyword
  4. Replace direct render call with prepareDesignEditor(designEditor)

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:

<Tabs storageKey="configuration"> <Tab name="Developer Portal"> To enable the Design Editor intent:

1. Navigate to an app via the Your apps page.
2. On the **Intents** page, find the "Available intents" section.
3. For the "Design Editor" intent, click **Set up**.
4. In the "Implement in your code" dialog that appears, click **Done**. This guide walks you through implementing the intent in your code.

</Tab>

<Tab name="Canva CLI"> 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.

   ```json
   {
     "intent": {
       "design_editor": {
         "enrolled": true
       }
     }
   }
   ```

3. Push the configuration to the Developer Portal:

   ```shell
   canva apps push
   ```

</Tab> </Tabs>

Advanced: Custom render implementation

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

tsx
import type { DesignEditorIntent } from "@canva/intents/design";
import { createRoot } from "react-dom/client";
import { AppUiProvider } from "@canva/app-ui-kit";
import { App } from "./app";
import "@canva/app-ui-kit/styles.css";

async function render() {
  // 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>
  );
};

const designEditor: DesignEditorIntent = { render };
export default designEditor; // default export the intent contract

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:

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

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:

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

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

Canva Developer Documentation SOP Site