Examples
App elements
Assets and media
Intents
Design interaction
Drag and drop
Design elements
Localization
Content replacement
Intent navigation example
Example app demonstrating navigating from Design Editor intent to the Content Publisher and Data Connector intents in bulk create.
Running this example
To run this example locally:
-
If you haven't already, create a new app in the Developer Portal(opens in a new tab or window). For more information, refer to our Quickstart guide.
-
In your app's configuration on the Developer Portal(opens in a new tab or window), ensure the "Development URL" is set to
http://localhost:8080. -
Clone the starter kit:
git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.gitcd canva-apps-sdk-starter-kitSHELL -
Install dependencies:
npm installSHELL -
Run the example:
npm run start intent_navigationSHELL -
Click the Preview URL link shown in the terminal to open the example in the Canva editor.
Example app source code
{"$schema": "https://www.canva.dev/schemas/app/v1/manifest-schema.json","manifest_schema_version": 1,"runtime": {"permissions": [{"name": "canva:design:content:read","type": "mandatory"},{"name": "canva:design:content:write","type": "mandatory"}]},"intent": {"data_connector": {"enrolled": true},"content_publisher": {"enrolled": true},"design_editor": {"enrolled": true}}}
JSON
// For usage information, see the README.md file.// This root index file contains the prepare function calls that initialize each intent.// Each intent entrypoint is respondible for exporting the intent contract implementation.import { prepareContentPublisher } from "@canva/intents/content";import { prepareDesignEditor } from "@canva/intents/design";import { prepareDataConnector } from "@canva/intents/data";import dataConnector from "./intents/data_connector";import contentPublisher from "./intents/content_publisher";import designEditor from "./intents/design_editor";prepareDataConnector(dataConnector);prepareContentPublisher(contentPublisher);prepareDesignEditor(designEditor);
TYPESCRIPT
// For usage information, see the README.md file.import type {ContentPublisherIntent,GetPublishConfigurationResponse,PublishContentRequest,PublishContentResponse,RenderPreviewUiRequest,RenderSettingsUiRequest,} from "@canva/intents/content";import { createRoot } from "react-dom/client";import "@canva/app-ui-kit/styles.css";import { AppUiProvider, Box } from "@canva/app-ui-kit";import { Rows, Text } from "@canva/app-ui-kit";import * as styles from "styles/components.css";// This example demonstrates how to launch Content Publisher Intent and Bulk Create with Data Connector Intent from within a Design Editor Intent.// For a more detailed example of the Content Publisher Intent, refer to the content_publisher_intent example.// Define the output types (publishing formats) available to users// Canva automatically displays a dropdown selector when more than one output type is definedasync function getPublishConfiguration(): Promise<GetPublishConfigurationResponse> {return {status: "completed",outputTypes: [{id: "post",displayName: "Output type: Post",mediaSlots: [{id: "media",displayName: "Media",fileCount: { exact: 1 },accepts: {image: {format: "png",// Social media post aspect ratio range (portrait to landscape)aspectRatio: { min: 4 / 5, max: 1.91 / 1 },},},},],},],};}// Render the settings UI where users configure publishing optionsfunction renderSettingsUi(request: RenderSettingsUiRequest) {const root = createRoot(document.getElementById("root") as Element);root.render(<AppUiProvider><div className={styles.scrollContainer}><Rows spacing="2u"><Text>This is the content publisher intent portion of the app. Here youwould render an interface for controlling the publishing settings.</Text></Rows></div></AppUiProvider>,);}// Render the preview UI showing how the content will appear after publishingfunction renderPreviewUi(request: RenderPreviewUiRequest) {const root = createRoot(document.getElementById("root") as Element);root.render(<AppUiProvider><div className={styles.previewContainer}><BoxclassName={styles.previewWrapper}background="surface"borderRadius="large"padding="2u"border="standard"><Rows spacing="2u"><Text>This is the content publisher intent portion of the app. Here youwould render a preview for visualising how the design would appearin the publishing platform.</Text></Rows></Box></div></AppUiProvider>,);}// Handle the actual publishing when the user clicks the publish button// In production, this should make API calls to your platformasync function publishContent(request: PublishContentRequest,): Promise<PublishContentResponse> {// Replace this with your actual API integration// Example: Upload media to your platform and create a post// const uploadedMedia = await uploadToYourPlatform(params.outputMedia);// const post = await createPostOnYourPlatform({// media: uploadedMedia,// caption: JSON.parse(params.publishRef).caption// });return {status: "completed",externalId: "1234567890", // Your platform's unique identifier for this postexternalUrl: "https://example.com/posts/1234567890", // Link to view the published content};}const contentPublisher: ContentPublisherIntent = {renderSettingsUi,renderPreviewUi,getPublishConfiguration,publishContent,};export default contentPublisher;
TYPESCRIPT
import type {DataConnectorIntent,GetDataTableRequest,GetDataTableResponse,RenderSelectionUiRequest,} from "@canva/intents/data";import { createRoot } from "react-dom/client";import { AppUiProvider, Rows, Text } from "@canva/app-ui-kit";import * as styles from "styles/components.css";// This example demonstrates how to launch Content Publisher Intent and Bulk Create with Data Connector Intent from within a Design Editor Intent.// For a more detailed example of the Data Connector Intent, refer to the data_connector_intent example.// Fetches data from external sources, transforms and returns the Canva's DataTable formatasync function getDataTable(request: GetDataTableRequest,): Promise<GetDataTableResponse> {const { signal } = request;// Check if the operation has been aborted.if (signal.aborted) {return {status: "app_error",message: "The data fetch operation was cancelled.",};}return {status: "completed",dataTable: {rows: [{ cells: [{ type: "string", value: "Fetched data" }] }],},};}// Renders a UI component for selecting and configuring data from external sources.async function renderSelectionUi(request: RenderSelectionUiRequest,): Promise<void> {const root = createRoot(document.getElementById("root") as Element);root.render(<AppUiProvider><div className={styles.scrollContainer}><Rows spacing="2u"><Text>This is the data connector intent portion of the app. Here you wouldrender an interface for controlling the data table.</Text></Rows></div></AppUiProvider>,);}const dataConnector: DataConnectorIntent = {getDataTable,renderSelectionUi,};export default dataConnector;
TYPESCRIPT
import "@canva/app-ui-kit/styles.css";import type { DesignEditorIntent } from "@canva/intents/design";import { AppUiProvider } from "@canva/app-ui-kit";import { createRoot } from "react-dom/client";import { Button, Rows, Text } from "@canva/app-ui-kit";import { publish, bulkCreate } from "@canva/design";import { useFeatureSupport } from "@canva/app-hooks";import * as styles from "styles/components.css";// This example demonstrates how to launch Content Publisher Intent and Bulk Create with Data Connector Intent from within a Design Editor Intent.// For a more detailed example of the Design Editor Intent and all its capabilities, refer to other app examples in this starter kit.const App = () => {const isSupported = useFeatureSupport();const bulkCreateSupported = isSupported(bulkCreate.launch);const publishSupported = isSupported(publish.launch);const launchBulkCreate = async () => {await bulkCreate.launch({ withDataConnector: "self" });};const launchContentPublisher = async () => {await publish.launch({ withContentPublisher: "self" });};return (<div className={styles.scrollContainer}><Rows spacing="2u"><Text>This is the design editor intent portion of the app.</Text>{bulkCreateSupported && (<Button variant="primary" onClick={launchBulkCreate}>Launch Bulk Create with Data Connector Intent</Button>)}{publishSupported && (<Button variant="primary" onClick={launchContentPublisher}>Launch Content Publisher Intent</Button>)}</Rows></div>);};async function render() {const root = createRoot(document.getElementById("root") as Element);// Render the design editor intent UIroot.render(<AppUiProvider><App /></AppUiProvider>,);}const designEditor: DesignEditorIntent = { render };export default designEditor;
TYPESCRIPT
# Intent NavigationThis example demonstrates how to structure an app to implement multiple intents in the same code and how to launch the content publisher and the data connector in bulk create from the design editor intent. The app is a combination of the design editor starter example, the data connector example and the content publisher example, with `bulkCreate.launch()` and `publish.launch()` calls.It is recommended to walk through the `implement_multiple_intents` example, to understand the concept of multiple intents in the same code, before stepping into navigating between intents.Similar to the `implement_multiple_intents` example, the root index file `src/index.tsx` calls the prepare function for each implemented intent.There's a `src/intents` folder, and within this folder there's one folder for each implemented intent. The index file of these folders implements the intent contract.For example, `src/index.tsx` calls `prepareDesignEditor` with the `DesignEditorIntent` implementation imported from `src/intents/design_editor/index.tsx`, calls `prepareDataConnector` with the `DataConnectorIntent` implementation imported from `src/intents/data_connector/index.tsx`, and calls `prepareContentPublisher` with the `ContentPublisherIntent` implementation imported from `src/intents/content_publisher/index.tsx`.In `src/intents/design_editor/index.tsx`, there are 2 buttons to launch the bulk create with data connector intent and to launch publish with content publisher intent. There is no way to launch any intent from inside the data connector intent or the content publisher intent, as an error will be thrown.This example also has a `canva-app.json` app config file with all related intents enrolled.NOTE: This example differs from what is expected for public apps to pass a Canva review:- Error handling is simplified for demonstration. Production apps must implement comprehensive error handling with clear user feedback and graceful failure modes- Accessibility features aren't fully implemented. Production apps must meet WCAG 2.0 AA standards with proper keyboard navigation and ARIA labels- Internationalization is not implemented. Production apps must support multiple languages using the `@canva/app-i18n-kit` package to pass Canva review requirements
MARKDOWN
API reference
- App UI Kit
bulkCreate.launchprepareContentPublisherprepareDataConnectorprepareDesignEditorpublish.launch
Need help?
- Join our Community Forum(opens in a new tab or window)
- Report issues with this example on GitHub(opens in a new tab or window)