How to Build a Drag-and-Drop Card Interface in React

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Fullstack React with TypeScript Masterclass course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Fullstack React with TypeScript Masterclass with a single-time purchase.

Thumbnail for the \newline course Fullstack React with TypeScript Masterclass
  • [00:00 - 00:11] Drag cards. It is time to drag the cards around. First we'll need to add a new action type. Open SRC state actions.ts and define a new move task action.

    [00:12 - 01:17] Update the action type add a new action with type move task. The payload should be an object where we are going to store dragged item.id string, hovered, item id string, or null, source, column id of type string and target column id of type string. Now define the action creator. Expert, const, move, task is a function. It should receive dragged item id of type string, hovered, item id of type string or null. Here we want to match the defined action type, source, column id of type string and target column id of type string. Here we want to return an object with type move task and payload with all the fields from arguments. Drag item id, hovered, item id, source, column id, and target column id.

    [01:18 - 01:35] Now open drag item.ts, define the card drag item, expert type, card drag item. You should have id of type string, column id of type string, text, also string, and type, card.

    [01:36 - 02:36] Now update the drag item definition by saying that it's a union of column drag item and card drag item. Now let's update the card component. Open card.tsx will want to get the column id of type string and ease preview because now we'll be able to drag it. So we want to be able to set it as a preview item. It's an optional flag of type boolean. Now get these new props inside of this component, text id, column id is preview, pass those values inside of the column component to the card component, text key id, and we also need to pass the column id. It's going to equal the id of the column that renders this card. Go back to the card. Now we'll want to use upstate here, const, drag item, dispatch from use upstate. We also want to define ref, const ref equals use ref HTML div element.

    [02:37 - 03:08] We know that it's a div because the card container that will be the reference is actually a styled div element. The default value here is now, and then we pass ref to our card container. We also want to pass the ease preview from the props and ease hidden that will calculate using the ease hidden function, where we'll pass the dragged item. That type is going to be card here, we'll pass the id and the ease preview flag, import the ease hidden function.

    [03:09 - 05:15] And now we can use the use item drag hook to make the cards dragable. const drag equals use item drag, where we'll pass that type card id, text, and column id. Cards should be dropable items too, so that when you hover some card with the card you're dragging, you should be able to drop the card in the place of the card you're hovering. So let's define drop method using use drop, it should accept only cards, I accept card, hover here, we'll need to throttle this function just because of the way how event triggering works when you hover something. It's just going to happen too frequently, and we might run into some race condition issues. So to avoid it, we throttle by 200 milliseconds, a callback, where we'll first check that if we don't have a dragged item, then we immediately return, otherwise we check if dragged item type is not card, then we also return, otherwise if the dragged item is the same item we're hovering, if dragged item id equals id of this item, then we also return, and then if we have a dragged item, if it is a card, if it's not the same card as we're hovering, then we dispatch move task, where we pass dragged item id, id of this element, dragged item, column id, and the column id. After we move the items, we need to update the dragged item. So we dispatch another action, set dragged item, and here we pass an object with the old dragged item, and we only update the column id, import the action creators, the throttle function that we use here comes from the throttle debauns TS. After we have both drag and drop methods, before the layout called drag, pass the drop methods to it, and inside of it pass the ref.