Multiple Intents Example

Example app demonstrating design editor and content publisher intents together

Running this example

To run this example locally:

  1. 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.

  2. 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.

  3. Clone the starter kit:

    git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.git
    cd canva-apps-sdk-starter-kit
    SHELL
  4. Install dependencies:

    npm install
    SHELL
  5. Run the example:

    npm run start implement_multiple_intents
    SHELL
  6. 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 defined
async 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 options
function 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 you
would 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 publishing
function renderPreviewUi(request: RenderPreviewUiRequest) {
const root = createRoot(document.getElementById("root") as Element);
root.render(
<AppUiProvider>
<div className={styles.previewContainer}>
<Box
className={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 you
would render a preview for visualising how the design would appear
in 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 platform
async 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 post
externalUrl: "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 intents
This 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?