Multiple intents
Example app demonstrating design editor and content publisher intents together
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:example implement_multiple_intentsSHELL -
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": {"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 contentPublisher from "./intents/content_publisher";import designEditor from "./intents/design_editor";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, Button } from "@canva/app-ui-kit";import { Rows, Text } from "@canva/app-ui-kit";import * as styles from "styles/components.css";import { requestOpenExternalUrl } from "@canva/platform";// This example demonstrates how multiple intents can be implemented within a single Canva app.// 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);const openDocs = async () => {await requestOpenExternalUrl({url: "https://www.canva.dev/docs/apps/content-publisher/",});};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><Button variant="primary" onClick={openDocs}>Open Content Publisher Intent docs</Button></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);// TODO: You should update this value to match the visuals you're hoping to achieve with your export preview// Image width + padding + borderconst previewWidth = 400 + 32 + 2;root.render(<AppUiProvider><Boxdisplay="flex"alignItems="center"justifyContent="center"flexDirection="column"width="full"height="full"><div style={{ width: previewWidth }}><Boxdisplay="flex"alignItems="center"justifyContent="center"background="surface"borderRadius="large"padding="2u"border="standard"><Rows spacing="2u"><Text>This is the content publisher intent portion of the app. Hereyou would render a preview for visualizing how the design wouldappear in the publishing platform.</Text></Rows></Box></div></Box></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 "@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 { requestOpenExternalUrl } from "@canva/platform";import * as styles from "styles/components.css";// This example demonstrates how multiple intents can be implemented within a single Canva app.// For a more detailed example of the Design Editor Intent and all its capabilities, refer to other app examples in this starter kit.async function render() {const root = createRoot(document.getElementById("root") as Element);const openDocs = async () => {await requestOpenExternalUrl({url: "https://www.canva.dev/docs/apps/design-editor/",});};// Render the design editor intent UI// This is a simple example to demonstrate how multiple intents can be implemented in the same app.root.render(<AppUiProvider><div className={styles.scrollContainer}><Rows spacing="2u"><Text>This is the design editor intent portion of the app.</Text><Button variant="primary" onClick={openDocs}>Open Design Editor Intent docs</Button></Rows></div></AppUiProvider>,);}const designEditor: DesignEditorIntent = { render };export default designEditor;
TYPESCRIPT
# Implement Multiple intentsThis example demonstrates how to structure an app to implement multiple intents in the same code. The app is a combination of the design editor starter example and the content publisher example.This example demonstrates our recommeneded code structure for multiple intent apps. 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`, and calls `prepareContentPublisher` with the `ContentPublisherIntent` implementation imported from `src/intents/content_publisher/index.tsx`.This example has a `canva-app.json` app config file with both intents enrolled.For API reference docs and instructions on running this example, see: <https://www.canva.dev/docs/apps/examples/multiple-intents/>.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
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)