> ## Content Index
> Fetch the complete content index at: https://blog.afi.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to use Shopify Draggable with React
- URL: https://blog.afi.io/blog/how-to-use-shopify-draggable-with-react/
- Published: 2023-06-13T06:12:18.000Z
- Updated: 2025-08-15T18:25:37.000Z
- Description: Refactoring Shopify Draggable code for use in a React web application.
- Author: Afian Anwar
- Tags: tech

In my [last post](https://afi.io/blog/using-shopify-draggable-to-build-a-drag-and-drop-timeline/?ref=blog.afi.io), I explained how to use the [@shopify/draggable](https://github.com/Shopify/draggable?ref=blog.afi.io) javascript library to build an interactive drag and drop timeline. In this one, I'll build on what we did in the previous post and show you how to refactor our code for use in a React web application.

Part 1: [Using Shopify Draggable to build a drag and drop timeline](https://afi.io/blog/using-shopify-draggable-to-build-a-drag-and-drop-timeline/?ref=blog.afi.io)  
**Part 2: How to use Shopify Draggable with React (this article)**  
Part 3: [Styling a Shopify Draggable React component with CSS](https://afi.io/blog/styling-a-shopify-draggable-react-component-with-css/?ref=blog.afi.io)

Here's what the interactive drag and drop timeline you'll get at the end of this tutorial looks like:

![Shopify draggable react javascript example](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/06/shopify-draggable-example.gif)

As with all our tutorials, you can find [working sample code for the full project ](https://github.com/afilabs/shopify%5Fdraggable%5Freactjs?ref=blog.afi.io)on Github.

[React](https://github.com/facebook/react?ref=blog.afi.io), built and maintained by Facebook, is the most popular frontend javascript framework for building user interfaces on the web. One of the biggest headaches with using [Shopify Draggable](https://shopify.github.io/draggable/?ref=blog.afi.io) was that it did not play well with React. Shopify Draggable works by updating the order of [HTML DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document%5FObject%5FModel/Introduction?ref=blog.afi.io) elements on the fly. Since these updates are not communicated to React, re renders cause the state of the Draggable elements to reset, so e.g. if you dragged a block from one spot to another, this would trigger a component rerender and the block would be "rewound" back into place.

> [Afi Labs](https://www.afi.io/?ref=blog.afi.io) delivers custom software to meet your logistics needs. From vehicle routing, live tracking to proof of delivery, we've got you covered!   
> 
> [👋 Say Hello!](https://afi.io/?ref=blog.afi.io#contact-us) 

There are two ways to solve this problem. The first is to use [wuweiweiwu/react-shopify-react](https://github.com/wuweiweiwu/react-shopify-draggable?ref=blog.afi.io) (not part of the official Shopify Draggable library) which fixes this by providing a <DraggableContainer/> React component that represents the Draggable container that with draggable elements. You can initialize this component with the specific Draggable functionality (e.g. [Dropable](https://github.com/Shopify/draggable/tree/master/src/Droppable?ref=blog.afi.io), [Draggable](https://github.com/Shopify/draggable?ref=blog.afi.io), [Sortable](https://github.com/Shopify/draggable/tree/master/src/Sortable?ref=blog.afi.io) etc) you want and they will automatically be passed (deeply) to the Draggable children via [React Context](https://react.dev/learn/passing-data-deeply-with-context?ref=blog.afi.io). The second (which this writeup is based on), is to initialize a separate `<SortableList/>` component with functionality for sorting a list of draggable elements using the "@shopify/draggable" library.

### How our code is organized

We scaffolded this project using the [Create React App](https://create-react-app.dev/?ref=blog.afi.io) with code that follows this default folder structure:

```html
my-app/
  README.md
  node_modules/
  package.json
  public/
    index.html
    favicon.ico
  src/
    App.css
    App.js
    App.test.js
    SortableList.js
    index.css
    index.js
    logo.svg
```

The only new file is `SortableList.js` which we'll explain in just a second. `public/index.html` is the page template and `src/index.js` is the JavaScript entry point. It renders the `<App/>` component `src/App.js` where most of new code resides.

### SortableList.js

Open `src/SortableList.js` and take a look inside (it's quite short).

```
import { Sortable } from "@shopify/draggable";

let sortable = null;

const SortableList = ({
  draggableSelector,
  indexAttribute,
  onSort,
  children,
}) => {
  const initSortable = (containerEl) => {
    if (sortable) return;

    sortable = new Sortable(containerEl, {
      draggable: draggableSelector,
    }).on("sortable:sorted", onDragSorted);
  };

  const onDragSorted = ({ data }) => {
    const { dragEvent } = data;
    const { over, source } = dragEvent;

    const overIndex = over.getAttribute(indexAttribute);
    const sourceIndex = source.getAttribute(indexAttribute);

    source.setAttribute(indexAttribute, overIndex);

    onSort(sourceIndex, overIndex);
  };

  return children({ initSortable: initSortable });
};

export default SortableList;
```

Here's a line by line explanation of what's going on:

```
import { Sortable } from "@shopify/draggable";
let sortable = null;
```

This imports the Sortable class from the @shopify/draggable library, which allows us to create sortable lists. For now, we initialize the sortable variable as null until it holds the instance of the Sortable class.

```
const SortableList = ({
	draggableSelector,
	indexAttribute,
	onSort,
	children,
}) => {
	const initSortable = (containerEl) => {
		if (sortable) return;

		sortable = new Sortable(containerEl, {
			draggable: draggableSelector,
		}).on("sortable:sorted", onDragSorted);
	};
	// Some extra code here ...
};
```

Next, we define the `SortableList` component as a function that takes an object with several props: `draggableSelector`, `indexAttribute`, `onSort`, and `children`. To understand what these are, let's take an advanced peek in `App.js` and see how `SortableList` is declared.

App.js

```
<SortableList
          draggableSelector=".dragItem"
          indexAttribute="data-index"
          onSort={handleSwap}
>
```

So this tells us that:

| **prop**          | **description**                                                                       |
| ----------------- | ------------------------------------------------------------------------------------- |
| draggableSelector | Any HTML element (in our example, its <li>) with the .dragItem class                  |
| indexAttribute    | The component prop "data-index" (used to store the order in which the element appears |
| onSort            | A function that points to handleSwap()                                                |

Back in `SortableList.js`, we see that the `initSortable` function (within the `SortableList` component) defined thus:

It takes as input a `containerEl` "container element" parameter for the sortable list and creates a new `Sortable` instance with the provided `containerEl` and sets the `draggable` option to the value of `draggableSelector` (the <li> elements we want to move around). Lastly, it registers an event listener for the ["sortable:sorted" event](https://github.com/Shopify/draggable/tree/master/src/Sortable/SortableEvent?ref=blog.afi.io), which calls the `onDragSorted()` function shown below.

```
const onDragSorted = ({
   data
}) => {

   console.log(data);

   const {
      dragEvent
   } = data;
   const {
      over,
      source
   } = dragEvent;

   const overIndex = over.getAttribute(indexAttribute);
   const sourceIndex = source.getAttribute(indexAttribute);

   source.setAttribute(indexAttribute, overIndex);

   onSort(sourceIndex, overIndex);
};

return children({
   initSortable: initSortable
});
};
```

The `data` object in the `onDragSorted()` method comes from the event object passed to the event listener when the "sortable:sorted" event is triggered. The `Sortable` class from the "@shopify/draggable" library emits this event when a draggable element is sorted within the list. Since this [SortableEvent](https://github.com/Shopify/draggable/blob/master/src/Sortable/SortableEvent/SortableEvent.js?ref=blog.afi.io) inherits from the [shared/AbstractEvent class](https://github.com/Shopify/draggable/blob/master/src/shared/AbstractEvent/AbstractEvent.js?ref=blog.afi.io), it comes with a `data` object passed into its constructor i.e.

AbstractEvent.js

```
/**
   * AbstractEvent constructor.
   * @constructs AbstractEvent
   * @param {object} data - Event data
   */
  constructor(data) {
    this[canceled] = false;
    this.data = data;
  }
```

The rest of the code in `onDragSorted()` handles the swapping. It checks for the current block's index (the block you are dragging) against the index of the block you are dragging over and swaps them by calling `onSort()`.

```
const {
   dragEvent
} = data;
const {
   over,
   source
} = dragEvent;

const overIndex = over.getAttribute(indexAttribute);
const sourceIndex = source.getAttribute(indexAttribute);

source.setAttribute(indexAttribute, overIndex);

onSort(sourceIndex, overIndex);
```

![Swapping mechanism in our shopify draggable react example](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/06/Screen-Shot-2023-06-13-at-1.56.56-PM.png)

The blue block being dragged "source" has an index of 1 because it was in second position counting from 0\. It is dragged over the yellow block "over" and their positions are swapped.

The `onSort()` method (originally named `handleSwap()`) was passed to `SortableList.js` from `App.js`. Here's what it does:

```
const [blocks, setBlocks] = useState(initialBlocks);

const handleSwap = (oldIndex, newIndex) => {
   [blocks[oldIndex], blocks[newIndex]] = [blocks[newIndex], blocks[oldIndex]];
   setBlocks([...blocks]);
};
```

It simply switches `oldIndex` with `newIndex` so blocks switch position whenever one is dragged over another (note: for this to work correctly, the blocks must physically pass over one another). 

### App.js

Turning over to `App.js`, we can see it's simply a wrapper for `SortableList`, which is imported right at the top with `import SortableList from "./SortableList";`. The `initialBlocks` array sets up our blocks with the original colors while the main chunk of code starting with `return ( ...` renders the `SortableList` component with the associated block colors and numbered order.

```
import React, {
   useState
} from "react";
import "./App.css";
import SortableList from "./SortableList";

const initialBlocks = [{
      bgColor: "#00a8ff",
      text: "lorem"
   },
   {
      bgColor: "#9c88ff",
      text: "ipsum"
   },
   {
      bgColor: "#fbc531",
      text: "dolor"
   },
   {
      bgColor: "#4cd137",
      text: "sit"
   },
   {
      bgColor: "#487eb0",
      text: "amet"
   },
];

function App() {
   const [blocks, setBlocks] = useState(initialBlocks);

   const handleSwap = (oldIndex, newIndex) => {
      [blocks[oldIndex], blocks[newIndex]] = [blocks[newIndex], blocks[oldIndex]];
      setBlocks([...blocks]);
   };

   return ( <
      div className = "container" >
      <
      ul className = "horizontal-menu sortable" >
      <
      SortableList draggableSelector = ".dragItem"
      indexAttribute = "data-index"
      onSort = {
         handleSwap
      } >
      {
         ({
            initSortable
         }) => (

            <
            div ref = {
               initSortable
            } > {
               console.log("running initSortable")
            } {
               blocks.map(({
                  bgColor,
                  text
               }, i) => ( <
                  li className = "dragItem"
                  data - index = {
                     i
                  }
                  key = {
                     text
                  }
                  style = {
                     {
                        backgroundColor: bgColor
                     }
                  } >
                  {
                     `${text} ${i + 1}`
                  } <
                  /li>
               ))
            } <
            /div>
         )
      } <
      /SortableList> <
      /ul> <
      /div>
   );
}

export default App;
```

To run this code, all you need to do is fork it from github and run `npm install` followed by `npm start`.

*And that's a wrap*! We've refactored our code and integrated our interactive drag and drop timeline we built in the [previous post](https://www.afi.io/blog/using-shopify-draggable-to-build-a-drag-and-drop-timeline/?ref=blog.afi.io) into a React application. In the [next post](https://afi.io/blog/styling-a-shopify-draggable-react-component-with-css/?ref=blog.afi.io), we'll add some css styling to make it look similar to the one in [Parcelizer](http://www.parcelizer.com/?ref=blog.afi.io) (below).

![Interactive drag drop timeline in Parcelizer built using Shopify Draggable React code](https://storage.ghost.io/c/c6/4d/c64da7e8-63a6-4cff-acdc-2782a6ebc377/content/images/2023/06/Screen-Shot-2023-06-13-at-3.10.37-PM.png)

Interactive drag drop timeline in Parcelizer built using Shopify Draggable React code

👋 **As always, if you have any questions or suggestions for me, please** [**reach out**](https://yourls.afi.io/newcontact?ref=blog.afi.io) **or** [**say hello on LinkedIn**](https://www.linkedin.com/in/afian-anwar-a023b143/?ref=the-afi-labs-blog)**.**

Next: [Styling a Shopify Draggable React component with CSS](https://afi.io/blog/styling-a-shopify-draggable-react-component-with-css/?ref=blog.afi.io)