snow-mountain

Using Drag and Drop in React Apps With sortableJS


Last updated on
ReactWeb Development

Summary (TL;DR): In this tutorial, we’re going to learn how to use SortableJS in creating and reordering lists with drag and drop functionality in React applications. We’d be building a project management application using React sortableJS for its drag and drop functions in moving projects stages (To-DO, Doing, and Done); by doing this, we’d master the use of React sortableJS.

Over the years, functionalities like drag-and-drop, sorting, and reordering of lists have become an essential part of many applications. The React ecosystem has grown with the invention of libraries that aid in developing applications with these functionalities. 
In this tutorial, we will learn how to use React sortableJS for developing sortable lists, reordering lists, and drag-and-drop. We’d understand its core concepts, some of its use cases and build a simple example. It is important to note that React sortableJS is open-source with more than 22k stars on GitHub.
This tutorial will help readers interested in developing drag-and-drop functionalities in their React applications using sortableJS. This article requires a basic understanding of React and TypeScript. 

Start Developing Amazing Apps on Common Ninja’s Developer Platform

Table of Contents

What Is SortableJS
Intro Into React SortableJS
Why React SortableJS?
Building a React Project Management Application
>> Setting Up Your Environment
>> Building the Board Component
>> Creating a Tile Component
>> Completing Our App

What Is SortableJS?

SortableJs is a JavaScript library that enables users to sort lists by dragging and dropping the list items.
SortableJS provides support for modern browsers, which includes a fallback function for non HTML5 browsers. With this, developers can test drag-and-drop behavior in older browsers and make it more consistent in desktop and mobile applications. 
SortableJS also supports modern JavaScript frameworks and libraries like React, Vue, Ember, and many other CSS libraries like Bootstrap and tailwind CSS. 
Like many drag and drop libraries, SortableJS prominent shortcoming is in the area of accessibilty, react sortableJS lacks accessibility support and SortableJS is only accessible to Mouse (and touch) users, I’d would refrain from using it for critical functionality, you can also check out other drag n drop alternatives.
In this tutorial, we will use React sortableJS, a wrapping component that provides React bindings for SortableJS to build a Trello clone with drag-and-drop functionalities. Users will be to switch between a list of items and move their project cards using drag-and-drop.

Intro Into React SortableJS

React sortableJS is a React binding for SortableJS, used for drag and drop functions and arranging lists. With React sortableJS, React developers can use sortableJS in React applications. It also provides support for TypeScript and also has inbuilt support for intellisense out of the box. To use React sortableJS, first, you’d need to install the package using NPM or yarn, like the code block below 

npm i react-sortablejs sortablejs

Or:

yarn add react-sortablejs sortablejs 

From the above, you can see we also installed sortableJS, and this is because React-sortableJS provides bindings for SortableJS and provides a wrapper component for SortableJS. To use React-sortableJS in a TypeScript project, we’d need to define it in our package installation using the command below 

npm install @types/sortablejs

To use React-sortableJS in a React project, you need to import react-sortable. Let’s build a simple, sortable component below to explain.

import React, { FC, useState } from "react";
import { ReactSortable } from "react-sortablejs";

interface ListType {
 id: number;
 name: string;
}

export const Scroll: FC = (props) => {
 const [state, setState] = useState<ListType[]>([
  { id: 1, name: "Peace" },
  { id: 2, name: "Soma-damian" },
  { id: 3, name: "Lee" },
 ]);

return (
 <ReactSortable list={state} setList={setState}>
  {state.map((item) => (
    <div key={item.id}>{item.name} {item.description}</div>
   ))}
  </ReactSortable>
 );
};

In the code above, first, we imported React’s useState and FC type. We also imported ReactSortable from the react-sortable package into your file. Next, we initialized an interface ListType which defines our id as a number, name as a string, and a description which is also a string. 
Using the FC package we imported from react, we created a functional component Scroll to add our data as objects. Using JavaScript map object, we loop through the data object we created in the Scroll component above.

Why React SortableJS?

React sortableJS makes it easy for React developers to use JavaScript’s sortableJS, its primary purpose is to provide bindings for sortableJS. It’s a wrapper component used for SortableJS.
React sortableJS also provides plugins such as TypeScript support, MultiDrag , and Swap out of the React applications box. With this, developers can build multi-drag functionalities in their applications. Let’s see in that code block.

import React, { FC, useState } from "react";
import { ReactSortable, Sortable, MultiDrag } from "react-sortablejs";

Sortable.mount(new MultiDrag());

const BodyInfo = () => {
 const [state, setState] = useState([
  { id: 1, name: "Peace", description: "Peace is amazing" },
  { id: 2, name: "Soma-damian", description: "I am Soma" },
  { id: 3, name: "Lee", description: "Lee is a doctor" },
 ]);

return (
 <ReactSortable multiDrag>
  {state.map((item) => (
   <div key={item.id}>{item.name} {item.description}</div>
  ))}
 </ReactSortable>
 );
};

In the code above, we imported Sortable and MultiDrag objects from react-sortablejs. To use the objects, we first initialize them in our application. Using a functional component, we created a BodyInfo component with data objects. We loop through the data objects, the JavaScript map object, to create multi drag for our list items using the ReactSortable component. A demo can be found in the video below.

Building a React Project Management Application

In this section, we are going to build a React project management application (Trello Clone) with React sortableJS and TypeScript. It should feature an input field, project sections with titles, backlog, Todo for projects user wants to do, Doing for projects the user is doing, and Done for projects done by the user. We’d be using React-sortableJS to add drag-and-drop functionality, Tailwind CSS, and postcss for styling our application.

Setting Up Your Environment

First, let’s create a bare React application, write the code below on your terminal. 

create-react-app sortable-trello-clone

The above code will create a bare React application using the create-react-app package. Navigate into the project directory 

cd sortable-trello-clone

Next is to install the dependencies we’d need in our project. 

yarn add react-sortablejs sortablejs @types/sortablejs postcss tailwind

In the above code block, we installed react-sortablejs, sortablejs @types/Ssortablejs, which sortableJS provides for typescript support, postcss , and Tailwind will be used for styling our application. If you’ve done this, then start the project server using the command below. 

yarn start

We’ll need an input field board component that can be dragged and dropped across our different project boards for this project. In this component, we’d create interfaces for our boards, cards, and using React hooks. We’d build input fields for the user to input their projects, and we’d also use React sortable to make it draggable. Let’s do that below. 

Building the Board Component

This component will contain a Card component, an input field for users to input a project card, and a submit button to submit any card a user input. In the src directory of your application, create a new folder called components; inside the folder, create another folder named Board and inside this folder, create a new file called Board.tsx. If you’ve done this, let’s build the logic for our component below.

import { useState } from "react";
import Tile from "../Tile/Tile";
import { ReactSortable } from "react-sortablejs";

interface IBoardProps {
    title: string;
}
interface ICard {
    id: number;
    text: string;
}
const Board: React.FC<IBoardProps> = ({ title }) => {
    const [showForm, setShowForm] = useState(false);
    const [text, setText] = useState<string>("");
    const [cards, setCards] = useState<ICard[]>([]);
    const handleSubmit = (e: { preventDefault: () => void }) => {
        e.preventDefault();
        const card = {
            id: cards.length + 1,
            text,
        };
        setCards([...cards, card]);
        setText("");
    };

First, we imported react-sortable and created an interface for our BoardProps and Card objects. We will be expecting a string for our Board, and for our Card object, we’d be expecting an id of number and a text of string. 
First, we created a functional Board component using TypeScript’s Reac.FC module and passing the title of our cards as props. We created a function handleSubmit that submits our cards to the boards. Inside it, the card object specifies the data to be added to our board input field, then we’d use a react useState hook to add a new card to the list of cards we presently have in a board. 
Let’s finish our Board component by creating our card input fields below. 

return (
        <div
            className="bg-gray-200 w-11/12 sm:w-10/12 
                md:w-80 h-full pb-3 rounded md:mr-6 mb-6">
            <h2 className="px-4 py-2 text-center 
                text-black text-lg font-bold">
                {title}
            </h2>
            <article id="list" className="mx-2">
                <ReactSortable
                    group="shared"
                    animation={200}
                    delay={1}
                    swap
                    multiDrag
                    setList={setCards}
                    list={cards}
                >
                    {cards.map((card: ICard) => (
                        <Tile key={card.id}>{card.text}</Tile>
                    ))}
                </ReactSortable>
            </article>
            {!showForm && (
                <button
                    style={{ outline: "none", border: "none" }}
                    className="text-sm ml-2 mt-1 
            text-blue-500 hover:bg-blue-200 transition-all px-3 py-1"
                    type="button"
                    onClick={() => setShowForm(true)}>
                    + Add another card
                </button>
            )}
            {showForm && (
                <form onSubmit={handleSubmit} 
                    className="w-full px-2 my-2">
                    <textarea
                        style={{ border: "none", outline: "none" }}
                        className="overflow-y-hidden block w-full 
                        resize-none py-2 px-2 text-sm rounded-sm"
                        name="card-task"
                        id="card-text"
                        value={text}
                        onChange={(e) => setText(e.target.value)}
                    />
                    <div className="flex items-center ml-1 mt-3">
                        <button
                            style={{ outline: "none", 
                            border: "none" }}
                            className="block text-sm mr-5 rounded-md
                            text-white bg-blue-500 hover:bg-blue-400 
                            transition-all px-3 py-2"
                            type="submit">
                            Add Card
                        </button>
                        <button
                            style={{ outline: "none", 
                            border: "none" }}
                            className="block text-4xl text-blue-500"
                            type="submit"
                            onClick={() => setShowForm(false)}>
                            &times;
                        </button>
                    </div>
                </form>
            )}
        </div>
    );
};
export default Board;

Using Tailwind classes in the code block above, we created a primary card component and input field for our Board cards. First, we created a title for our Board using the h2 tag. We then made use of ReactSortable component. The group prop allows a user to move a card across different boards, in our case a user can move a card across our boards like Backlog, To Do, Doing and Done. In the component above, we passed a number of props to the ReactSortable component, they include:

  • Animation: this prop is used to add animations to our cards, with it we can define the length of the animation, in our case we added a value of 200.
  • swap: is the prop from sortableJS that allows us to reorder the positions of our cards in a board.
  • multiDrag: the multiDrag prop allows us to drag multiple cards across boards.

To add our card to our list of cards in a board, we initialised our setList hook which we already defined at the beginning of our component with  React’s useState state.
To render our component, we use the map object to loop through our current list of cards. Next, we create a button to add a new card and another to submit our current card to a Board. Our present component will through an error; this is because we imported an empty component, Tile. Let’s create it below.

Creating a Tile Component

This component will expect children’s prop; inside it, we’d be using React.FC to assign type interface to the component. It’s a child component that will add text to our cards for our Board component. Let’s create it below 

import React from "react";

interface ITileProps{
    children: React.ReactNode;
}

const Tile:React.FC<ITileProps> = ({children}) => {
    return (
        <div className='bg-white py-2 px-2 mb-2 
            transition-shadow shadow-lg rounded'>
            <p className='text-sm'>{children}</p>
        </div>
    );
};

export default Tile;

In the code block above, we created a Tile component with children props to pass text to our cards.

Completing Our App

Our App.tsx file will contain our Board components and project timelines such as Backlog, TODOs, DOING, etc.; we will be using Tailwind CSS for styling. Let’s do this below. 

import Board from "./components/Board/Board";
const App = () => {
    return (
        <div
            style={{ background: "#0079bf", maxWidth: "1440px" }}
            className="w-screen relative pt-4 max-h-full h-screen"
        >
            <div className="w-11/12 mx-auto relative z-50">
                <h1 className="text-2xl md:text-4xl text-blue-300 mb-6 font-bold text-center ">
                    Trello Clone
                </h1>
                <div className="flex lg:justify-between justify-center flex-wrap lg:flex-nowrap">
                    <Board title="Backlog" />
                    <Board title="TO DOs" />
                    <Board title="DOING" />
                    <Board title="DONE" />
                </div>
            </div>
        </div>
    );
};
export default App; 

In this component, first, we added our App title using an h1 tag. Next, we created Boards for our application using the Board component we created. If done correctly, our App should look like the video below.

Conclusion

In this article, we learned about SortableJS, a JavaScript library that enables a user to sort items and add drag-and-drop functionalities to applications. We also learned about React sortableJS, a wrapping component library that provides bindings for sortableJS. We went through the process of creating a project management tool using React sortableJS and TypeScript. You could make the project management application accessible by allowing users to drag-and-drop items using the keyboard and announcing changes for screen reader users with ARIA live regions.

Have a look at Dragon Drop for inspiration and guidance.

The code for the project management application can be found on GitHub.