Feature support

How apps can adapt to their current context.

Apps can run in different contexts. For example, apps can run in different design types, such as presentations and documents, and on different surfaces, such as the object panel and selected image overlays.

Some features of the Apps SDK, however, are only available in certain contexts.

Whether or not a feature is supported in the current context depends on a variety of factors, including:

  • The design type of the current design.
  • The surface on which the app is mounted.

In the future, additional factors may be introduced.

Something we want to avoid is requiring apps to implement complex logic to determine whether or not a feature is available in the current context. As much as possible, we want to abstract away this complexity.

To allow for this, we've included a useFeatureSupport hook in the starter kit:

import { Button, Rows } from "@canva/app-ui-kit";
import { addElementAtCursor, addElementAtPoint } from "@canva/design";
import { useFeatureSupport } from "utils/use_feature_support"; // https://github.com/canva-sdks/canva-apps-sdk-starter-kit/blob/main/utils/use_feature_support.ts
import * as styles from "styles/components.css";
export const App = () => {
const isSupported = useFeatureSupport();
function handleAddElementAtPoint() {
if (!isSupported(addElementAtPoint)) {
return;
}
addElementAtPoint({
type: "text",
children: ["Hello world"],
});
}
function handleAddElementAtCursor() {
if (!isSupported(addElementAtCursor)) {
return;
}
addElementAtCursor({
type: "text",
children: ["Hello world"],
});
}
return (
<div className={styles.scrollContainer}>
<Rows spacing="1u">
<Button
variant="primary"
onClick={handleAddElementAtPoint}
disabled={!isSupported(addElementAtPoint)}
>
Add element at point
</Button>
<Button
variant="primary"
onClick={handleAddElementAtCursor}
disabled={!isSupported(addElementAtCursor)}
>
Add element at cursor
</Button>
</Rows>
</div>
);
};
tsx

This hook returns an isSupported function that:

  • Accepts a method as its only argument.
  • Returns true if the specified method is not available in the current context.

By using this function:

  • You don't have to think about the conditional logic. It's "invisible" to the app.
  • Your app will automatically adapt if the conditional logic changes.

Any method can be checked for compatibility, but only some methods need checking. To learn which methods need to be checked, see the Feature support matrix.

In the future, there will be situations where the availability of a feature may change while an app is open. If you're using the useFeatureSupport hook, this is not something you need to think about, as the isSupported function is reactive and automatically listens for these changes.

If you're not using the hook though, you'll need to register a callback that listens for these changes:

import { Button, Rows } from "@canva/app-ui-kit";
import { addElementAtCursor, addElementAtPoint } from "@canva/design";
import type { Feature } from "@canva/platform";
import { features } from "@canva/platform";
import { useEffect, useState } from "react";
import * as styles from "styles/components.css";
export const App = () => {
const [isSupported, setIsSupported] = useState(() => {
return (...args: Feature[]) => features.isSupported(...args);
});
// Listen for feature support changes
useEffect(() => {
return features.registerOnSupportChange(() => {
setIsSupported(() => {
return (...args: Feature[]) => features.isSupported(...args);
});
});
}, []);
function handleAddElementAtPoint() {
addElementAtPoint({
type: "text",
children: ["Hello world"],
});
}
function handleAddElementAtCursor() {
addElementAtCursor({
type: "text",
children: ["Hello world"],
});
}
return (
<div className={styles.scrollContainer}>
<Rows spacing="1u">
<Button
variant="primary"
onClick={handleAddElementAtPoint}
disabled={!isSupported(addElementAtPoint)}
>
Add element at point
</Button>
<Button
variant="primary"
onClick={handleAddElementAtCursor}
disabled={!isSupported(addElementAtCursor)}
>
Add element at cursor
</Button>
</Rows>
</div>
);
};
tsx

Apps are only properly compatible with different contexts if they listen for these changes.

The useFeatureSupport hook is not type-safe. The same is true for the methods it calls. This is because features are checked for compatibility at runtime, not at compile time.

This is important because it means that an app compiling without TypeScript errors is not a guarantee that the app will run in the browser.

Therefore, we recommend:

  • Manually testing your app in a variety of contexts, such as in different design types.
  • Not relying on type-checking to catch feature compatibility errors.

This section lists all of the context-sensitive methods in the Apps SDK.

You can use this information to:

  • Identify when you need to use the useFeatureSupport hook.
  • Identify when you need to test your app in different contexts.

Only context-sensitive methods are shown in this table. If a method is not listed in the table, assume that it's available in all contexts. Additionally, this table doesn't show deprecated methods.

MethodFixed pagesResponsive designsOverlays
openColorSelector
requestFontSelection
MethodFixed pagesResponsive designsOverlays
addAudioTrack
addElementAtCursor
addElementAtPoint
addPage
getCurrentPageBackground
getCurrentPageContext
getDefaultPageDimensions
overlay
requestExport
startDrag
startDragToCursor
startDragToPoint
MethodFixed pagesResponsive designsOverlays
initOauth