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
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)}>
×
</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.