Feature support

Feature detection and conditional functionality based on platform support.

Running this example

To run this example locally:

  1. 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.

  2. 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.

  3. Clone the starter kit:

    git clone https://github.com/canva-sdks/canva-apps-sdk-starter-kit.git
    cd canva-apps-sdk-starter-kit
    SHELL
  4. Install dependencies:

    npm install
    SHELL
  5. Run the example:

    npm run start feature_support
    SHELL
  6. 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 { addElementAtPoint, addPage } from "@canva/design";
import { useState } from "react";
import * as styles from "styles/components.css";
import { useFeatureSupport } from "utils/use_feature_support";
import { HomePage } from "./home";
import { InteractionPage } from "./interaction";
type AppPage = "home" | "interaction";
/**
* Top-level component that manages state across the example app.
**/
export const App = () => {
const [appPage, setAppPage] = useState<AppPage>("home");
// A new callback is returned each time the feature support profile of the
// current page changes in Canva.
const isSupported = useFeatureSupport();
// Check whether `addElementAtPoint` and `addPage` are supported in the current page.
const isInteractionSupported = isSupported(addElementAtPoint, addPage);
const renderPage = (page: AppPage) => {
switch (page) {
case "home":
return (
<HomePage enterInteractionPage={() => setAppPage("interaction")} />
);
break;
case "interaction":
return (
<InteractionPage
goBack={() => setAppPage("home")}
isInteractionSupported={isInteractionSupported}
/>
);
default:
return;
}
};
return <div className={styles.scrollContainer}>{renderPage(appPage)}</div>;
};
TYPESCRIPT
import { Button, Rows, Text, Title } from "@canva/app-ui-kit";
type HomePageProps = {
enterInteractionPage: () => void;
};
/**
* Home Page component containing page controls
**/
export const HomePage = (props: HomePageProps) => {
return (
<Rows spacing="1.5u">
<Title>Home page</Title>
<Text>
This example app demonstrates how to toggle interactive UI based on the
currently available features.
</Text>
<Button variant="primary" onClick={props.enterInteractionPage}>
Enter interactions page
</Button>
</Rows>
);
};
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
import { Alert, Button, Rows } from "@canva/app-ui-kit";
import { addElementAtPoint, addPage } from "@canva/design";
type InteractionPageProps = {
goBack: () => void;
isInteractionSupported: boolean;
};
/**
* A page component containing various buttons to interact with the design.
**/
export const InteractionPage = (props: InteractionPageProps) => {
return (
<Rows spacing="1.5u">
{!props.isInteractionSupported && <UnsupportedAlert />}
<Button variant="primary" onClick={props.goBack}>
Back
</Button>
<Button
disabled={!props.isInteractionSupported}
variant="secondary"
onClick={() => addElementAtPoint({ type: "shape", ...shape })}
>
Add a shape at a point
</Button>
<Button
disabled={!props.isInteractionSupported}
variant="secondary"
onClick={() =>
addElementAtPoint({
type: "group",
children: [
{
type: "shape",
...shape,
left: 0,
top: 0,
width: 200,
height: "auto",
},
{
type: "embed",
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
left: 300,
top: 100,
width: 200,
height: "auto",
},
],
})
}
>
Add a group at a point
</Button>
<Button
disabled={!props.isInteractionSupported}
variant="secondary"
onClick={() => addPage()}
>
Add a new page
</Button>
</Rows>
);
};
const shape = {
paths: [
{
d: "M 0 0 H 100 V 100 H 0 L 0 0",
fill: {
dropTarget: false,
color: "#ff0099",
},
},
],
viewBox: {
width: 100,
height: 100,
top: 0,
left: 0,
},
};
const UnsupportedAlert = () => (
<Alert tone="warn" title="Shapes, Groups and Pages can't be added to Docs.">
Try using this app in a different design type.
</Alert>
);
TYPESCRIPT
# Feature support
Demonstrates how to detect and handle feature availability across different design types and contexts. Shows dynamic feature detection, graceful degradation, and context-aware UI rendering.
For API reference docs and instructions on running this example, see: https://www.canva.dev/docs/apps/examples/feature-support/.
Related examples: This pattern should be used across all apps to ensure proper functionality across different design contexts.
NOTE: This example differs from what is expected for public apps to pass a Canva review:
- Feature detection patterns are simplified for demonstration purposes only. Production apps must implement feature detection for all functionality that depends on design context or user permissions
- 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?