Content Publisher

Publish Canva designs directly to external platforms.

Content Publisher is a type of intent that enables users to publish Canva designs directly to external platforms.

When users want to publish their designs to an external platform, they can configure their publish settings and preview the content before publishing.

The Content Publisher intent is provided as a preview. Preview intents are unstable and may change without warning. You can't release public apps using a preview intent until it's stable.

Our design guidelines help you create a high-quality app that easily passes app review.

Architectural overview

The following diagram demonstrates the Content Publisher workflow. There are two app iframe views: one for the publish settings UI and one for the preview UI. The publish intent platform handles the communication between the app views and Canva.

OutputType

The OutputType identifies a distinct type of output that a third-party platform expects. These could be formats like an Instagram Story or a YouTube Video, or outputs like a Mailchimp Email or an ad type on Meta Ads.

Key concepts:

  • OutputType: Defines a specific type of output that an app supports for an external platform (e.g., Instagram Reel, Mailchimp Email).
  • MediaSlot: Describes a grouping of one or more files required to produce that output.
  • FileRequirement: Defines constraints on those files (e.g., format, size, aspect ratio).

PublishRef

PublishRef is an opaque string to store all the settings you require to publish on your platform. Your app is responsible for serializing and deserializing this string. It is passed between settings, preview, and publishing steps, and has a maximum size of 5 KB.

PreviewMedia

PreviewMedia provides real-time file previews to render in your app's preview UI iframe during the publish flow. These previews update dynamically as users modify their publish settings.

Key concepts:

  • Preview Files: Media representations that can be in various states (loading, ready, error, and so on).
  • Video Optimization: Videos start as lightweight thumbnails and can be upgraded to full videos on demand.
  • Live Updates: Previews refresh automatically when users change settings.

OutputMedia

OutputMedia represents the final exported files that will be sent to an external platform during the publish operation. These are the production-ready files that match the requirements you specified in your app's OutputType configuration.

Key concepts:

  • OutputFiles: The actual media files exported from Canva designs.

Creating a content publisher

Step 1: Enable the intent

Before an intent can be implemented, it must be enabled. Enabling an intent informs Canva of when and where the app should be invoked throughout the user experience.

You can configure intents for your app with the Developer Portal:

  1. Navigate to an app via the Your apps(opens in a new tab or window) page.
  2. On the Compatibility page, find the Intents section.
  3. Toggle Content Publisher to the On position.

Step 2: Enable the required scopes

In the Apps SDK, certain methods require certain scopes to be enabled. If an app attempts to call methods without the required scopes being enabled, the SDK will throw an error.

The Content Publisher intent requires the following scopes:

  • Design read (canva:design:content:read)

To learn more, see Configuring scopes.

Step 3: Register the intent

Before an intent can be implemented, it must be registered. Registering the Content Publisher intent establishes how the content will be published and what UI to display for configuring the publish settings.

To register the intent, call the prepareContentPublisher method as soon as the app loads:

import { prepareContentPublisher } from "@canva/intents/content";
// Register the content publisher when your app loads
prepareContentPublisher({
getPublishConfiguration: async (params) => {
// Implement the logic to get the publish configuration
},
renderSettingsUi: (params) => {
// Implement the UI for settings view
},
renderPreviewUi: (params) => {
// Implement the UI for preview view
},
publishContent: async (params) => {
// Implement the logic to publish the content
},
});
TSX

Intents must only be registered once. To learn more, see Technical requirements.

Step 4: Provide output types

To provide the content formats your platform supports (for example, Instagram Reel, Post, Story) and their media requirements, use the getPublishConfiguration method.

const outputTypes: OutputType[] = [{
id: "post",
displayName: "Feed Post",
mediaSlots: [{
id: "media", displayName: "Media", fileCount: { exact: 1 }, accepts: { image: { format: "png", aspectRatio: { min: 4 / 5, max: 1.91 / 1 } } },
}]
}];
async function getPublishConfiguration(): Promise<GetPublishConfigurationResponse> {
return {
status: "completed",
outputTypes,
};
}
TYPESCRIPT

Step 5: Render publish settings UI

Renders your app's custom config UI for users to configure platform-specific settings (for example, captions, hashtags, privacy settings).

import type {
RenderSettingsUiRequest,
SettingsUiContext,
} from "@canva/intents/design";
import { FormField, Rows, Text, TextInput } from "@canva/app-ui-kit";
import { useEffect, useState } from "react";
import * as styles from "styles/components.css";
import type { PublishSettings } from "./types";
// Settings UI component for configuring publish settings
export const SettingUi = ({
updatePublishSettings,
registerOnSettingsUiContextChange,
}: RenderSettingsUiRequest) => {
const [settings, setSettings] = useState<PublishSettings>({
caption: "",
});
const [settingsUiContext, setSettingsUiContext] =
useState<SettingsUiContext | null>(null);
// Listen for settings UI context changes (e.g., when output type changes)
useEffect(() => {
const dispose = registerOnSettingsUiContextChange((context) => {
setSettingsUiContext(context);
});
return dispose;
}, [registerOnSettingsUiContextChange]);
// Update publish settings whenever they change
// This notifies Canva of the current settings and validity state
useEffect(() => {
updatePublishSettings({
publishRef: JSON.stringify(settings),
validityState: validatePublishRef(settings),
});
}, [settings, updatePublishSettings]);
return (
<div className={styles.scrollContainer}>
<Rows spacing="2u">
<Text>{settingsUiContext?.outputType.displayName}</Text>
<FormField
label="Caption"
control={(props) => (
<TextInput
{...props}
value={settings.caption}
onChange={(caption) =>
setSettings((prev) => ({ ...prev, caption }))
}
/>
)}
/>
</Rows>
</div>
);
};
// Validates the publish settings to enable/disable the publish button
// Returns "valid" when all required fields are filled
const validatePublishRef = (publishRef: PublishSettings) => {
// caption is required
if (publishRef.caption.length === 0) {
return "invalid_missing_required_fields";
}
return "valid";
};
const root = createRoot(document.getElementById("root") as Element);
prepareContentPublisher({
getPublishConfiguration,
// Render the settings UI where users configure publishing options
renderSettingsUi: ({
updatePublishSettings,
registerOnSettingsUiContextChange,
}) => {
root.render(
<AppUiProvider>
<SettingUi
updatePublishSettings={updatePublishSettings}
registerOnSettingsUiContextChange={registerOnSettingsUiContextChange}
/>
</AppUiProvider>,
);
},
renderPreviewUi: (params) => {
// Implement the UI for preview view
},
publishContent: async (params) => {
// Implement the logic to publish the content
},
});
TSX

Step 6: Render preview UI

Displays a live app preview UI showing how the content will appear on your target platform.

import type { OutputType, PreviewMedia } from "@canva/intents/content";
import { useEffect, useState } from "react";
import * as styles from "./preview_ui.css";
import type { PublishSettings } from "./types";
// Main preview UI component that receives preview updates when settings or pages change.
// preview UI is more flexible to align with your platform's design system, so it is not constrained to the Canva design system.
export const PreviewUi = ({ registerOnPreviewChange }: RenderPreviewUiRequest) => {
const [previewData, setPreviewData] = useState<{
previewMedia: PreviewMedia[];
outputType: OutputType;
publishRef?: string;
} | null>(null);
// Register to receive preview updates whenever settings or pages change
useEffect(() => {
const dispose = registerOnPreviewChange((data) => {
setPreviewData(data);
});
return dispose;
}, [registerOnPreviewChange]);
const { previewMedia, publishRef, outputType } = previewData ?? {};
const publishSettings = parsePublishSettings(publishRef);
return (
<div className={styles.container}>
{outputType?.id === "post" && (
<PostPreview previewMedia={previewMedia} settings={publishSettings} />
)}
</div>
);
};
// Renders a post preview that is specific to your platform
export const PostPreview = ({ previewMedia, settings }: {
previewMedia: PreviewMedia[] | undefined;
settings: PublishSettings | undefined;
}) => {
// Implement the logic to render the post preview
};
const root = createRoot(document.getElementById("root") as Element);
prepareContentPublisher({
getPublishConfiguration,
// Render the settings UI where users configure publishing options
renderSettingsUi: ({
updatePublishSettings,
registerOnSettingsUiContextChange,
}) => {
root.render(
<AppUiProvider>
<SettingUi
updatePublishSettings={updatePublishSettings}
registerOnSettingsUiContextChange={registerOnSettingsUiContextChange}
/>
</AppUiProvider>,
);
},
// Render the preview UI showing how the content will appear after publishing
renderPreviewUi: ({ registerOnPreviewChange }) => {
root.render(
<AppUiProvider>
<PreviewUi registerOnPreviewChange={registerOnPreviewChange} />
</AppUiProvider>,
);
},
publishContent: async (params) => {
// Implement the logic to publish the content
},
});
TSX

Step 7: Publish content

Use the publishContent method to publish the content to your platform, invoking your platform's APIs. The publishContent method is called when the user clicks the publish button.

async function publishContent(params: 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 root = createRoot(document.getElementById("root") as Element);
prepareContentPublisher({
getPublishConfiguration,
// Render the settings UI where users configure publishing options
renderSettingsUi: ({
updatePublishSettings,
registerOnSettingsUiContextChange,
}) => {
root.render(
<AppUiProvider>
<SettingUi
updatePublishSettings={updatePublishSettings}
registerOnSettingsUiContextChange={registerOnSettingsUiContextChange}
/>
</AppUiProvider>,
);
},
// Render the preview UI showing how the content will appear after publishing
renderPreviewUi: ({ registerOnPreviewChange }) => {
root.render(
<AppUiProvider>
<PreviewUi registerOnPreviewChange={registerOnPreviewChange} />
</AppUiProvider>,
);
},
publishContent,
});
TYPESCRIPT