Making things draggable

How to make anything draggable.

In the starter kit, we've included React components for creating draggable images and text:

These components abstract away some of the complexity of the Drag and Drop capability and, for most use-cases, we recommend using them instead of using the capability directly.

That said, there are some situations where these components aren't suitable:

  • You want to make something draggable that isn't an image or text
  • You're not using React

In these cases, you'll need to use the capability directly.

Step 1: Initialize the capability

Like all capabilities, Canva injects the Drag and Drop capability into the app's iframe and attaches it to the window object:

console.log(window.canva.dragAndDrop);

You can access the capability by importing and calling the getDragAndDrop function:

import { getDragAndDrop } from "@canva/dnd";
const dragAndDrop = getDragAndDrop();
console.log(dragAndDrop); // => DragAndDrop

This function:

  • Throws an error if the capability is unavailable
  • Provides type-safety when using TypeScript

Step 2: Create an HTML node

  1. Create an HTML node, such as a div, span, or img:

    import React from "react";
    import { getDragAndDrop } from "@canva/dnd";
    const App = () => {
    const dragAndDrop = getDragAndDrop();
    return <div>This will be made draggable.</div>;
    };

    This is the node that will be made draggable

  2. Add a draggable attribute to the node:

    <div draggable={true}>This will be made draggable.</div>

    The draggable attribute is a standard HTML feature that allows the node to be dragged, but additional steps are required to complete the integration with Canva.

Step 3: Grab a reference to the node

Use the useRef hook — or, if you're not using React, the document.querySelector method — to grab a reference to the node:

import React from "react";
import { getDragAndDrop } from "@canva/dnd";
const App = () => {
const dragAndDrop = getDragAndDrop();
const ref = React.useRef<HTMLDivElement>(null);
return (
<div ref={ref} draggable={true}>
This will be made draggable.
</div>
);
};

This lets the app directly interact with the node, which is necessary for the next step.

Step 4: Make the node draggable

The Drag and Drop capability exposes a makeDraggable method. It's this method that makes Canva aware of an HTML node as it's being dragged and dropped.

The method accepts an object with the following properties:

  • node - A reference to the HTML node that will be made draggable.
  • dragData - Options for customizing the behavior of the drag event.
  • onDragStart - A callback that runs at the start of the drag event.
  • onDragEnd - A callback that runs at the end of the drag event.

The dragData property is an object that defines the media to be added to the user's design at the end of a drag event. The expected properties in this object depends on the media type:

  • Images
  • Text
  • Videos

The following snippet demonstrates how to add an image to the user's design:

import React from "react";
import { getDragAndDrop } from "@canva/dnd";
const App = () => {
const dragAndDrop = getDragAndDrop();
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (!ref?.current) {
return;
}
dragAndDrop.makeDraggable({
node: ref.current,
dragData: {
type: "IMAGE",
fullSizeSrc: "DATA URL GOES HERE",
previewSize: {
width: 50,
height: 50,
},
},
onDragStart: () => {
console.log("The drag event has started");
},
onDragEnd: () => {
console.log("The drag event has ended");
},
});
}, [dragAndDrop, ref]);
return (
<div ref={ref} draggable={true}>
This will be made draggable.
</div>
);
};

Step 5: Handle drag events

The makeDraggable method accepts two callback functions:

  • onDragStart
  • onDragEnd

These callback functions run at the start and end of a drag event, respectively.

A use-case for these functions is to hide the draggable node at the start of a drag event and show it again at the end of a drag event. This creates the illusion of the node being moved from one location to another, rather than copied, which is consistent with the native Canva experience.