> ## 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. # Image editing overlay Create custom image editing overlays and filters. ## 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 image_editing_overlay ``` 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 { appProcess } from "@canva/platform"; import { ObjectPanel } from "./object_panel"; import { SelectedImageOverlay } from "./overlay"; export const App = () => { const context = appProcess.current.getInfo(); if (context.surface === "object_panel") { return ; } if (context.surface === "selected_image_overlay") { return ; } throw new Error(`Invalid surface: ${context.surface}`); }; ``` ```typescript // For usage information, see the README.md file. import { AppUiProvider } from "@canva/app-ui-kit"; import { createRoot } from "react-dom/client"; import "@canva/app-ui-kit/styles.css"; import { App } from "./app"; 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); } ``` ```typescript import { Alert, Button, Rows, Text, Title } from "@canva/app-ui-kit"; import { appProcess } from "@canva/platform"; import * as React from "react"; import * as styles from "styles/components.css"; import { useFeatureSupport, useOverlay } from "@canva/app-hooks"; export const ObjectPanel = () => { const overlay = useOverlay("image_selection"); const isSupported = useFeatureSupport(); const [isImageReady, setIsImageReady] = React.useState(false); React.useEffect(() => { // Listen for messages from the overlay about image readiness appProcess.registerOnMessage(async (sender, message) => { if ( typeof message === "object" && message != null && "isImageReady" in message ) { setIsImageReady(Boolean(message.isImageReady)); } }); }, []); const handleOpen = () => { overlay.open(); }; const handleInvert = () => { appProcess.broadcastMessage({ action: "invert" }); }; const handleBlur = () => { appProcess.broadcastMessage({ action: "blur" }); }; const handleReset = () => { appProcess.broadcastMessage({ action: "reset" }); }; const handleSave = () => { overlay.close({ reason: "completed" }); }; const handleClose = () => { overlay.close({ reason: "aborted" }); }; // Check if overlay functionality is supported if (!isSupported(overlay.open)) { return (
); } // Show overlay controls when overlay is open if (overlay.isOpen) { return (
Image editing Apply effects to your image with real-time preview.
); } // Show initial state with open overlay button return (
Image editing overlay Select a raster image in your design to start editing with real-time preview. {!overlay.canOpen && }
); }; // Alert shown when image overlay is not supported const UnsupportedAlert = () => ( Image editing overlay functionality is not supported in the current design type. ); // Alert shown when no valid image is selected const SelectionAlert = () => ( Select a single raster image in your design to enable image editing. Vector images and multiple selections are not supported. ); ```
```typescript import { getTemporaryUrl, upload } from "@canva/asset"; import { appProcess } from "@canva/platform"; import * as React from "react"; import { useSelection } from "@canva/app-hooks"; export const SelectedImageOverlay = () => { const selection = useSelection("image"); const canvasRef = React.useRef(null); const originalImageRef = React.useRef(null); React.useEffect(() => { const initializeCanvas = async () => { try { // Get the selected image const draft = await selection.read(); const [image] = draft.contents; if (!image) { return; } // Download the selected image const { url } = await getTemporaryUrl({ type: "image", ref: image.ref, }); const img = await downloadImage(url); // Store reference to original image for reset functionality originalImageRef.current = img; // Render the selected image const { canvas, context } = getCanvas(canvasRef.current); canvas.width = img.width; canvas.height = img.height; context.drawImage(img, 0, 0, img.width, img.height); // Notify that image is ready appProcess.broadcastMessage({ isImageReady: true }); } catch { appProcess.broadcastMessage({ isImageReady: false }); } }; initializeCanvas(); }, [selection]); React.useEffect(() => { // Listen for editing commands from the object panel appProcess.registerOnMessage(async (sender, message) => { if ( typeof message !== "object" || message == null || !("action" in message) ) { return; } try { const { canvas, context } = getCanvas(canvasRef.current); switch (message.action) { case "invert": context.filter = "invert(100%)"; context.drawImage(canvas, 0, 0); break; case "blur": context.filter = "blur(3px)"; context.drawImage(canvas, 0, 0); break; case "reset": if (originalImageRef.current) { context.filter = "none"; context.clearRect(0, 0, canvas.width, canvas.height); context.drawImage(originalImageRef.current, 0, 0); } break; default: // Unknown action, do nothing break; } } catch { // Silently handle effect application errors } }); }, []); React.useEffect(() => { // Handle overlay disposal (save or close) return void appProcess.current.setOnDispose(async (context) => { try { // Save changes if user completed the editing if (context.reason === "completed") { // Get the modified image data const { canvas } = getCanvas(canvasRef.current); const dataUrl = canvas.toDataURL("image/png", 1.0); // Upload the modified image const asset = await upload({ type: "image", mimeType: "image/png", url: dataUrl, thumbnailUrl: dataUrl, aiDisclosure: "none", }); // Replace the original image with the modified version const draft = await selection.read(); const [image] = draft.contents; if (!image) { return; } image.ref = asset.ref; await draft.save(); } // Reset image readiness state appProcess.broadcastMessage({ isImageReady: false }); } catch { // Handle save errors silently } }); }, [selection]); return ( ); }; // Utility function to download image from URL const downloadImage = async (url: string): Promise => { const response = await fetch(url, { mode: "cors" }); const blob = await response.blob(); const objectUrl = URL.createObjectURL(blob); const img = new Image(); img.crossOrigin = "anonymous"; await new Promise((resolve, reject) => { img.onload = () => resolve(); img.onerror = () => reject(new Error("Image could not be loaded")); img.src = objectUrl; }); URL.revokeObjectURL(objectUrl); return img; }; // Utility function to get canvas and context in a type-safe way const getCanvas = (canvas: HTMLCanvasElement | null) => { if (!canvas) { throw new Error("HTMLCanvasElement does not exist"); } const context = canvas.getContext("2d"); if (!context) { throw new Error("CanvasRenderingContext2D does not exist"); } return { canvas, context }; }; ``` ```markdown # Image editing overlay Demonstrates how to use image editing overlays to create simple interactive image editing experiences. Shows real-time preview of basic image effects, communication between overlay and object panel, and saving edited images back to the design. For API reference docs and instructions on running this example, see: https://www.canva.dev/docs/apps/examples/image-editing-overlay/. Related examples: See assets_and_media/asset_upload for image importing, or design_elements/image_elements for basic image manipulation patterns. NOTE: This example differs from what is expected for public apps to pass a Canva review: - ESLint rule `no-console` is disabled for example purposes only. Production apps shouldn't disable linting rules without proper justification - Image effects are simplified for demonstration. Production apps should implement more sophisticated image processing and comprehensive error handling - Internationalization isn't implemented. Production apps must support multiple languages using the `@canva/app-i18n-kit` package to pass Canva review requirements - The example uses basic image effects. Production apps should consider offering customizable parameters (e.g., blur radius, effect intensity) for better user experience - 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/) * [`appProcess.broadcastMessage`](https://www.canva.dev/docs/apps/api/latest/platform-app-process-broadcast-message/) * [`appProcess.current.getInfo`](https://www.canva.dev/docs/apps/api/latest/platform-app-process-current-get-info/) * [`appProcess.current.setOnDispose`](https://www.canva.dev/docs/apps/api/latest/platform-app-process-current-set-on-dispose/) * [`appProcess.registerOnMessage`](https://www.canva.dev/docs/apps/api/latest/platform-app-process-register-on-message/) * [`getTemporaryUrl`](https://www.canva.dev/docs/apps/api/latest/asset-get-temporary-url/) * [`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)