Examples
App elements
Assets and media
Intents
Design interaction
Drag and drop
Design elements
Localization
Content replacement
URL expander intent
URL expander integration for Canva AI to expand URLs to Canva supported assets.
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 url_expander_intentSHELL -
Click the Preview URL link shown in the terminal to open the example in the Canva editor.
Example app source code
import type { Asset } from "@canva/intents/asset";export interface BaseAsset {id: string;type: Asset["type"];name: string;url: string;mimeType: Asset["mimeType"];parentRef?: string;// Optional fields for specific asset typesthumbnailUrl?: string;thumbnailImageUrl?: string;}export const audioAssets: BaseAsset[] = [{id: "aud-1",type: "audio",name: `audio/wave`,url: `https://www.canva.dev/example-assets/audio-import/audio.wav`,mimeType: "audio/wave",parentRef: undefined,},{id: "aud-2",type: "audio",name: `audio/vnd.wave`,url: `https://www.canva.dev/example-assets/audio-import/audio.wav`,mimeType: "audio/vnd.wave",parentRef: undefined,},];export const imageAssets: BaseAsset[] = [{id: "img-1",type: "image",name: `image.heic`,url: `https://www.canva.dev/example-assets/image-import/sample3.heif`,thumbnailUrl: `https://www.canva.dev/example-assets/image-import/sample3thumbnail.heif`,mimeType: "image/heic",parentRef: undefined,},{id: "img-2",type: "image",name: `image.jpeg`,url: `https://www.canva.dev/example-assets/image-import/grass-image.jpg`,thumbnailUrl: `https://www.canva.dev/example-assets/image-import/grass-image-thumbnail.jpg`,mimeType: "image/jpeg",parentRef: undefined,},];export const documentAssets: BaseAsset[] = [{id: "doc-1",type: "document",name: `application/pdf`,url: `https://www.canva.dev/example-assets/document-import/document.pdf`,mimeType: "application/pdf",parentRef: undefined,},{id: "doc-2",type: "document",name: `application/vnd.openxmlformats-officedocument.wordprocessingml.document`,url: `https://www.canva.dev/example-assets/document-import/document.docx`,mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",parentRef: undefined,},];export const sheetAssets: BaseAsset[] = [{id: "sheet-1",type: "sheet",name: `text/csv`,url: `https://www.canva.dev/example-assets/sheet-import/sheet.csv`,mimeType: "text/csv",parentRef: undefined,},{id: "sheet-2",type: "sheet",name: `application/vnd.ms-excel`,url: `https://www.canva.dev/example-assets/sheet-import/sheet.xls`,mimeType: "application/vnd.ms-excel",parentRef: undefined,},];export const videoAssets: BaseAsset[] = [{id: "vid-1",type: "video",name: `video/avi`,url: `https://www.canva.dev/example-assets/video-import/video.avi`,thumbnailImageUrl: `https://www.canva.dev/example-assets/video-import/avi.png`,mimeType: "video/avi",parentRef: undefined,},{id: "vid-2",type: "video",name: `video/x-msvideo`,url: `https://www.canva.dev/example-assets/video-import/video.avi`,thumbnailImageUrl: `https://www.canva.dev/example-assets/video-import/avi.png`,mimeType: "video/x-msvideo",parentRef: undefined,},];export const genericAssets: BaseAsset[] = [{id: "gen-1",type: "generic",name: `zip`,url: `https://www.canva.dev/example-assets/generic-import/Archive.zip`,mimeType: "application/zip-compressed",parentRef: undefined,},];export const exampleAssets: BaseAsset[] = [...audioAssets,...imageAssets,...documentAssets,...videoAssets,...genericAssets,];
TYPESCRIPT
// For usage information, see the README.md file.import type {ExpandUrlRequest,ExpandUrlResponse,GetContentRequest,GetContentResponse,UrlExpanderIntent,} from "@canva/intents/asset";import { prepareUrlExpander } from "@canva/intents/asset";import { exampleAssets } from "./asset";import { mapBaseAssetToURLExpanderAsset } from "./mapper";/*** Retrieves the reference of an asset by an URL.** @param request - The request object containing the an URL, and the requestConnection callback.* @returns A promise resolving to the reference content or an error status*/async function expandUrl(request: ExpandUrlRequest,): Promise<ExpandUrlResponse> {/*** If your app requires OAuth to access user data, you can use the auth and oauth module to handle authentication.* Here's an example of how to request a connection and authorization:* ```* if (!await oauth.getAccessToken()) {* await request.requestConnection();* await oauth.requestAuthorization();* }* ```* Please await the completion of requests before proceeding with any further logic.* RequestConnection allows the user trigger the connection flow by themselves through the Canva UI.*/const foundAsset = exampleAssets.find((a) => a.url === request.url);if (foundAsset) {return {status: "completed",result: {ref: {type: "asset",id: foundAsset.id,name: foundAsset.name,iconUrl: foundAsset.url,description: foundAsset.mimeType,},},};} else {return { status: "not_found" };}}/*** Retrieves the full content of an asset by its reference ID.** @param request - The request object containing the asset reference, and the requestConnection callback.* @returns A promise resolving to the asset content or an error status*/async function getContent(request: GetContentRequest,): Promise<GetContentResponse> {const { ref } = request;/*** Implement a similar OAuth flow as shown in the expandUrl handler,* to ensure you can retrieve the token when getContent is being called independently.* Please await the completion of requests before proceeding with any further logic.* RequestConnection allows the user trigger the connection flow by themselves through the Canva UI.*/const foundAsset = exampleAssets.find((asset) => asset.id === ref.id);if (!foundAsset) {return {status: "app_error",message: "Asset not found",};}return {status: "completed",result: {type: "asset",asset: mapBaseAssetToURLExpanderAsset(foundAsset),},};}// Configure the URL Expander intent with required callbacksconst urlExpander: UrlExpanderIntent = {expandUrl,getContent,};prepareUrlExpander(urlExpander);
TYPESCRIPT
import type {Asset,AudioAsset,DocumentAsset,ImageAsset,SheetAsset,VideoAsset,} from "@canva/intents/asset";import type { BaseAsset } from "./asset";export function mapBaseAssetToURLExpanderAsset(asset: BaseAsset): Asset {switch (asset.type) {case "audio":return {type: "audio",name: asset.name,url: asset.url,mimeType: asset.mimeType as AudioAsset["mimeType"],};case "image":return {type: "image",name: asset.name,url: asset.url,thumbnailUrl: asset.thumbnailUrl || "",mimeType: asset.mimeType as ImageAsset["mimeType"],};case "video":return {type: "video",name: asset.name,url: asset.url,thumbnailImageUrl: asset.thumbnailImageUrl || asset.thumbnailUrl || "",mimeType: asset.mimeType as VideoAsset["mimeType"],};case "document":return {type: "document",name: asset.name,url: asset.url,mimeType: asset.mimeType as DocumentAsset["mimeType"],};case "sheet":return {type: "sheet",name: asset.name,url: asset.url,mimeType: asset.mimeType as SheetAsset["mimeType"],};case "generic":return {type: "generic",name: asset.name,url: asset.url,mimeType: asset.mimeType,};default:throw new Error(`Unsupported asset type: ${asset.type}`);}}
TYPESCRIPT
# URL expander intentThis example demonstrates how to build a URL expander intent app that allows users to paste URLs from external platforms and have the app automatically fetch and import the associated assets into Canva designs. The app shows how to implement both the URL expansion logic and content retrieval for various asset types.For API reference docs and instructions on running this example, see: https://www.canva.dev/docs/apps/examples/url-expander-intent/.NOTE: This example differs from what is expected for public apps to pass a Canva review:- **Static assets**: This example uses static Canva-hosted URLs for media content. Production apps should use CDN/hosting services and implement the `upload` function from `@canva/asset` package for real media uploads.- **OAuth authentication**: The example includes commented-out OAuth patterns but doesn't implement actual authentication. Production apps should implement proper OAuth flows using the `@canva/app-oauth` package to access user data from external platforms.- **API integration**: This example uses mock data. Production apps need to implement proper API authentication, rate limiting, and error handling for external data sources.- **Localization**: Text content is hardcoded in English. Production apps require proper internationalization using the `@canva/app-i18n-kit` package for multi-language support.- **Error handling**: Production apps should have comprehensive error handling for network failures, API errors, invalid URLs, and unsupported asset types.- **Code structure**: The code structure is simplified. Production apps using [intents](https://www.canva.dev/docs/apps/intents/) are recommended to call the prepareUrlExpander function from src/intents/url_expander/index.tsx
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)