Drag and drop

How apps can support drag and drop.

Something that Canva users love is the ability to drag and drop content users straight into their designs. Apps can use the Apps SDK to support this behavior, ensuring that the user experience is delightfully consistent.

Our design guidelines help you create a high-quality app that easily passes app review.

Apps can enable drag and drop for various types of content, including:

Additional content types may be supported in the future.

Apps need to render something in their UI that can be dragged.

This can be as simple as an HTMLDivElement with a draggable attribute:

<div draggable>This text can be dragged.</div>
tsx

In the App UI Kit though, we've also provided a number of card components that are designed to work seamlessly with drag and drop. The components include:

  • AudioCard
  • EmbedCard
  • ImageCard
  • TypographyCard
  • VideoCard

To see how to use these components, see Code samples.

Apps can handle drag events by registering an onDragStart callback and passing the drag event into either of the following methods:

  • startDragAtPoint
  • startDragAtCursor

Both methods add content to the user's design, but:

  • startDragAtPoint is only compatible with design types that support absolute positions, which is all design types except for documents.
  • startDragAtCursor is only compatible with design types that contain streams of text, which is only the document design type.

The supported content types also depend on the method being called:

Content TypestartDragAtPointstartDragAtCursor
Audio tracks
Embeds
Images
Text
Videos

Where possible, apps should determine the context in which the app is running and either call the compatible method or make it obvious when functionality isn't available. To learn more, see Feature support.

For accessibility reasons, drag and drop should not be the only way that users can add content to a design. Apps should also support click events. This behavior is built into the App UI Kit components and demonstrated below.

import React from "react";
import { AudioCard, AudioContextProvider, Rows } from "@canva/app-ui-kit";
import { upload } from "@canva/asset";
import { addAudioTrack, ui } from "@canva/design";
import { useFeatureSupport } from "utils/use_feature_support";
import styles from "styles/components.css";
export function App() {
const isSupported = useFeatureSupport();
async function handleClick() {
if (isSupported(addAudioTrack)) {
const asset = await upload({
type: "audio",
title: "Example audio",
mimeType: "audio/mp3",
url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",
});
addAudioTrack({
ref: asset.ref,
});
}
}
function handleDragStart(event: React.DragEvent<HTMLElement>) {
if (isSupported(ui.startDragAtPoint)) {
ui.startDragAtPoint(event, {
type: "audio",
title: "Example audio",
resolveAudioRef: () => {
return upload({
type: "audio",
title: "Example audio",
mimeType: "audio/mp3",
url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",
});
},
});
}
if (isSupported(ui.startDragAtCursor)) {
ui.startDragAtCursor(event, {
type: "audio",
title: "Example audio",
resolveAudioRef: () => {
return upload({
type: "audio",
title: "Example audio",
mimeType: "audio/mp3",
url: "https://www.canva.dev/example-assets/audio-import/audio.mp3",
});
},
});
}
}
return (
<div className={styles.scrollContainer}>
<Rows spacing="1u">
{/* A single `AudioContextProvider` component must be an ancestor to all `AudioCard` components*/}
<AudioContextProvider>
<AudioCard
title="Example audio"
audioPreviewUrl="https://www.canva.dev/example-assets/audio-import/audio.mp3"
durationInSeconds={86}
onClick={handleClick}
onDragStart={handleDragStart}
/>
</AudioContextProvider>
</Rows>
</div>
);
}
tsx