> ## Documentation Index > Fetch the complete documentation index at: https://www.canva.dev/docs/apps/llms.txt > Use this file to discover all available pages before exploring further. # Page addition Add new pages with pre-populated elements to designs. ## Running this example To run this example locally: 1. If you haven't already, create a new app in the [Developer Portal](https://www.canva.com/developers/apps). For more information, refer to our [Quickstart guide](https://www.canva.dev/docs/apps/quickstart/). 2. In your app's configuration on the [Developer Portal](https://www.canva.com/developers/apps), ensure the "Development URL" is set to `http://localhost:8080`. 3. Clone the starter kit: ```shell git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.git cd canva-apps-sdk-starter-kit ``` 4. Install dependencies: ```shell npm install ``` 5. Run the example: ```shell npm run start:example page_addition ``` 6. Click the **Preview URL** link shown in the terminal to open the example in the Canva editor. ## Example app source code ```typescript // For usage information, see the README.md file. import { Alert, Button, FormField, MultilineInput, Rows, Text, } from "@canva/app-ui-kit"; import type { Dimensions, EmbedElement, GroupElement, PageMetadata, } 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 "@canva/app-hooks"; // Constants for element sizing and positioning const IMAGE_ELEMENT_WIDTH = 50; const IMAGE_ELEMENT_HEIGHT = 50; const TEXT_ELEMENT_WIDTH = 130; // Scale factors to size elements relative to page dimensions const HEADER_ELEMENT_SCALE_FACTOR = 0.2; const EMBED_ELEMENT_SCALE_FACTOR = 0.4; const createHeaderElement = async (): Promise => { // Upload the image asset to Canva's asset management system // This returns a reference that can be used in design elements const { ref } = await upload({ mimeType: "image/png", thumbnailUrl: weather, type: "image", url: weather, width: 100, height: 100, aiDisclosure: "none", }); // Create a group element containing an image and text // Groups allow multiple elements to be positioned and scaled together return { type: "group", children: [ { type: "image", ref, // Reference to the uploaded asset 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, }, ], }; }; // Define an embed element that will display a YouTube video // Embed elements allow integration of external content like videos, social media posts, etc. const embedElement: EmbedElement = { type: "embed", url: "https://www.youtube.com/watch?v=tBe79N-4zm4", }; export const App = () => { const [error, setError] = useState(); const [isLoading, setIsLoading] = useState(false); const [defaultPageDimensions, setDefaultPageDimensions] = useState< Dimensions | undefined >(); // Metadata returned by addPage, including the new page's stable id const [addedPageMetadata, setAddedPageMetadata] = useState< PageMetadata | undefined >(); // Check if the current design type supports the addPage feature // Some design types like docs may not support page addition const isSupported = useFeatureSupport(); const isRequiredFeatureSupported = isSupported(addPage); useEffect(() => { // Get design metadata to determine if page addition is supported // and retrieve the default page dimensions for element positioning 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(); // Add a new page to the current design with pre-populated elements // The addPage API returns PageMetadata for the new page, including a // stable id (PageId) when the page type is "absolute". const pageMetadata = await addPage({ title: "Weather forecast", elements: [ { ...headerElement, width: headerElementWidth, height: "auto", // Position elements relative to page dimensions for responsive layout // Shift from the top by 10% top: defaultPageDimensions.height * 0.1, // Center horizontally: 50% page width minus 50% element width left: defaultPageDimensions.width / 2 - headerElementWidth / 2, }, { ...embedElement, width: embedElementWidth, height: "auto", // Shift from the top by 40% top: defaultPageDimensions.height * 0.4, // Center horizontally: 50% page width minus 50% element width left: defaultPageDimensions.width / 2 - embedElementWidth / 2, }, ], }); setAddedPageMetadata(pageMetadata); setError(undefined); } catch (e) { // Handle Canva-specific errors that can occur during page addition 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 (
This example demonstrates how apps can add a new page with pre-populated elements. The returned page metadata includes a stable page ID. {error && {error}} {!isRequiredFeatureSupported && } ( )} />
); }; const UnsupportedAlert = () => ( Sorry, the required features are not supported in the current design. ); ```
```typescript // For usage information, see the README.md file. import { AppUiProvider } from "@canva/app-ui-kit"; import { createRoot } from "react-dom/client"; import { App } from "./app"; import "@canva/app-ui-kit/styles.css"; import type { DesignEditorIntent } from "@canva/intents/design"; import { prepareDesignEditor } from "@canva/intents/design"; async function render() { const root = createRoot(document.getElementById("root") as Element); root.render( , ); } const designEditor: DesignEditorIntent = { render }; prepareDesignEditor(designEditor); // Hot Module Replacement for development (automatically reloads the app when changes are made) if (module.hot) { module.hot.accept("./app", render); } ``` ```markdown # Page addition Demonstrates how to add new pages to designs with pre-populated content including images, text, and embed elements. Shows page creation, content layout, multi-element page composition, and the page metadata (including stable page ID) returned by `addPage`. 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 - The code structure is simplified: Production apps using [intents](https://www.canva.dev/docs/apps/intents/) are recommended to call the prepareDesignEditor function from src/intents/design_editor/index.tsx ```
## API reference * [App UI Kit](https://www.canva.dev/docs/apps/app-ui-kit/) * [`addPage`](https://www.canva.dev/docs/apps/api/latest/design-add-page/) * [`getDesignMetadata`](https://www.canva.dev/docs/apps/api/latest/design-get-design-metadata/) * [`prepareDesignEditor`](https://www.canva.dev/docs/apps/api/latest/intents-design-prepare-design-editor/) * [`upload`](https://www.canva.dev/docs/apps/api/latest/asset-upload/) ## Need help? * Join our [Community Forum](https://community.canva.dev/) * Report issues with this example on [GitHub](https://github.com/canva-sdks/canva-apps-sdk-starter-kit/issues)