Examples
App elements
Assets and media
Fundamentals
Intents
Design interaction
Drag and drop
Design elements
Localization
Content replacement
Page addition
Add new pages with pre-populated elements to designs.
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 page_additionSHELL -
Click the Preview URL link shown in the terminal to open the example in the Canva editor.
Example app source code
// For usage information, see the README.md file.import { Alert, Button, Rows, Text } from "@canva/app-ui-kit";import type { Dimensions, EmbedElement, GroupElement } from "@canva/design";import { addPage, getDesignMetadata } from "@canva/design";import { CanvaError } from "@canva/error";/*** Static images are used here for demonstration purposes only.* In a real app, you should use a CDN/hosting service to host your images,* then upload them to Canva using the `upload` function from the `@canva/asset` package.*//* eslint-disable-next-line no-restricted-imports */import weather from "assets/images/weather.png";import { useState, useEffect } from "react";import * as styles from "styles/components.css";import { upload } from "@canva/asset";import { useFeatureSupport } from "utils/use_feature_support";const IMAGE_ELEMENT_WIDTH = 50;const IMAGE_ELEMENT_HEIGHT = 50;const TEXT_ELEMENT_WIDTH = 130;const HEADER_ELEMENT_SCALE_FACTOR = 0.2;const EMBED_ELEMENT_SCALE_FACTOR = 0.4;const createHeaderElement = async (): Promise<GroupElement> => {const { ref } = await upload({mimeType: "image/png",thumbnailUrl: weather,type: "image",url: weather,width: 100,height: 100,aiDisclosure: "none",});return {type: "group",children: [{type: "image",ref,top: 0,left: 0,width: IMAGE_ELEMENT_WIDTH,height: IMAGE_ELEMENT_HEIGHT,altText: {text: "weather forecast photo",decorative: undefined,},},{type: "text",children: ["Weather Forecast"],top: IMAGE_ELEMENT_HEIGHT,left: IMAGE_ELEMENT_WIDTH / 2 - TEXT_ELEMENT_WIDTH / 2,width: TEXT_ELEMENT_WIDTH,},],};};const embedElement: EmbedElement = {type: "embed",url: "https://www.youtube.com/watch?v=tBe79N-4zm4",};export const App = () => {const [error, setError] = useState<string | undefined>();const [isLoading, setIsLoading] = useState(false);const [defaultPageDimensions, setDefaultPageDimensions] = useState<Dimensions | undefined>();const isSupported = useFeatureSupport();const isRequiredFeatureSupported = isSupported(addPage);useEffect(() => {getDesignMetadata().then(({ defaultPageDimensions }) => {// Dimensions are undefined if the user is in an unbounded design (e.g. Whiteboard).if (!defaultPageDimensions) {setError("Adding pages in unbounded documents, such as Whiteboards, is not supported.",);}setDefaultPageDimensions(defaultPageDimensions);});}, []);const addNewPage = async () => {setIsLoading(true);try {// Dimensions are undefined if the user is in an unbounded design (e.g. Whiteboard).if (!defaultPageDimensions) {return;}setError(undefined);const headerElementWidth =defaultPageDimensions.width * HEADER_ELEMENT_SCALE_FACTOR;const embedElementWidth =defaultPageDimensions.width * EMBED_ELEMENT_SCALE_FACTOR;const headerElement = await createHeaderElement();await addPage({title: "Weather forecast",elements: [{...headerElement,width: headerElementWidth,height: "auto",// Shift from the top by 10%top: defaultPageDimensions.height * 0.1,// Shift it by 50% of the page width, then subtract 50% of the group element width.left: defaultPageDimensions.width / 2 - headerElementWidth / 2,},{...embedElement,width: embedElementWidth,height: "auto",// Shift from the top by 40%top: defaultPageDimensions.height * 0.4,// Shift it by 50% of the page width, then subtract 50% of the group element width.left: defaultPageDimensions.width / 2 - embedElementWidth / 2,},],});setError(undefined);} catch (e) {if (e instanceof CanvaError) {switch (e.code) {case "quota_exceeded":setError("Sorry, you cannot add any more pages. Please remove an existing page and try again.",);break;case "rate_limited":setError("Sorry, you can only add up to 3 pages per second. Please try again.",);break;default:setError(e.message);break;}}} finally {setIsLoading(false);}};return (<div className={styles.scrollContainer}><Rows spacing="3u"><Text>This example demonstrates how apps can add a new page withpre-populated elements.</Text>{error && <Text tone="critical">{error}</Text>}<Buttonvariant="primary"onClick={addNewPage}stretch// Default page dimensions are undefined in unbounded designs, so the button remains disabled.// Add page is not supported in certain design type such as docs.disabled={!defaultPageDimensions || !isRequiredFeatureSupported}loading={isLoading}>Add page</Button>{!isRequiredFeatureSupported && <UnsupportedAlert />}</Rows></div>);};const UnsupportedAlert = () => (<Alert tone="warn">Sorry, the required features are not supported in the current design.</Alert>);
TYPESCRIPT
import { AppUiProvider } from "@canva/app-ui-kit";import { createRoot } from "react-dom/client";import { App } from "./app";import "@canva/app-ui-kit/styles.css";const root = createRoot(document.getElementById("root") as Element);function render() {root.render(<AppUiProvider><App /></AppUiProvider>,);}render();if (module.hot) {module.hot.accept("./app", render);}
TYPESCRIPT
# Page additionDemonstrates how to add new pages to designs with pre-populated content including images, text, and embed elements. Shows page creation, content layout, and multi-element page composition.For API reference docs and instructions on running this example, see: https://www.canva.dev/docs/apps/examples/page-addition/.Related examples: See design_interaction/design_editing for page content manipulation, or design_elements examples for individual element creation patterns.NOTE: This example differs from what is expected for public apps to pass a Canva review:- Static assets are used for demonstration purposes only. Production apps should host assets on a CDN/hosting service and use the `upload` function from the `@canva/asset` package- ESLint rule `no-restricted-imports` is disabled for example purposes only. Production apps should not disable linting rules without proper justification- Error handling is simplified for demonstration. Production apps must implement comprehensive error handling with clear user feedback and graceful failure modes- 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)