Skip to content
Drag & Drop

Hello Pangea DnD — Component Library Guide

The maintained fork of react-beautiful-dnd, keeping accessible keyboard dragging and natural motion alive on modern React after Atlassian stopped.

TypeScript Apache-2.0
3.1M weekly downloads
4.0k GitHub stars
30.8 kB min+gzip bundle size
v18.0.1 latest version

Figures above measured on from the npm registry (latest version, last-week downloads) and the GitHub API (stars). Not copied from the project's own README, and re-measured rather than edited by hand, so the date tells you exactly how old these numbers are.

How the bundle size was measured. We installed @hello-pangea/dnd@18.0.1 on its own, bundled export * from '@hello-pangea/dnd' with esbuild (esm, browser, minified, production), marked React external because your app already has it, and gzipped the result. Measured . This is the ceiling, not the typical cost: it imports the entire public surface with no tree-shaking credit. Pull one component from a well-shaken library and you'll pay a fraction of it. We publish the ceiling because it's the number we can reproduce, and reproducing it is the point.

drag-and-droplistskanbanaccessibilitykeyboard

TL;DR: @hello-pangea/dnd is the maintained continuation of react-beautiful-dnd, with the same API on current React. Best-in-class keyboard and screen-reader support for list reordering. You own the state: onDragEnd gives indices, you produce the new array.

What is Hello Pangea DnD?

Atlassian's react-beautiful-dnd set the standard for list drag-and-drop in React, then stopped being maintained and was archived in 2023, stranding a large number of applications on a package that didn't support React 18.

This fork picked it up. It kept the API, ported the source to TypeScript, added support for React 18 and 19, and has been shipping releases since. For most codebases the upgrade path is changing one string in the import.

When to use it

Use it for lists. Kanban columns, reorderable playlists, a settings screen where the user arranges their own order, a form builder with a field list. Anything where items live in one dimension and move within or between containers.

Use dnd-kit when the interaction isn't list-shaped: free positioning on a canvas, custom collision rules, dragging between fundamentally different surfaces. Neither library is a general answer.

Key Features

  • Keyboard dragging out of the box, with space to lift and arrows to move
  • Screen-reader announcements at every step of a drag, with overridable text
  • Movement driven by transforms only, so dragging never triggers layout
  • Multi-list dragging with per-list drop rules
  • Virtual list support for long collections
  • Types included, so no separate @types package

Installation

npm install @hello-pangea/dnd

Migrating from the original is two steps:

npm uninstall react-beautiful-dnd @types/react-beautiful-dnd
npm install @hello-pangea/dnd

Then replace from 'react-beautiful-dnd' with from '@hello-pangea/dnd'.

A Reorderable List

import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
import type { DropResult } from '@hello-pangea/dnd';
import { useState } from 'react';

type Task = { id: string; title: string };

export function TaskList({ initial }: { initial: Task[] }) {
  const [tasks, setTasks] = useState(initial);

  function onDragEnd(result: DropResult): void {
    if (!result.destination) { return; }
    if (result.destination.index === result.source.index) { return; }
    const next = Array.from(tasks);
    const [moved] = next.splice(result.source.index, 1);
    next.splice(result.destination.index, 0, moved);
    setTasks(next);
  }

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="tasks">
        {provided => (
          <ul ref={provided.innerRef} {...provided.droppableProps}>
            {tasks.map((task, index) => (
              <Draggable key={task.id} draggableId={task.id} index={index}>
                {dragProvided => (
                  <li
                    ref={dragProvided.innerRef}
                    {...dragProvided.draggableProps}
                    {...dragProvided.dragHandleProps}
                  >
                    {task.title}
                  </li>
                )}
              </Draggable>
            ))}
            {provided.placeholder}
          </ul>
        )}
      </Droppable>
    </DragDropContext>
  );
}

Three details do most of the work. provided.placeholder holds the gap open while an item is lifted; without it the list collapses and jumps. draggableId must be a string and must be stable across renders, so an array index will not do. And onDragEnd returning early on a null destination is what makes a drop outside the list a no-op rather than a crash.

Separating the Handle

Split dragHandleProps off the item to make only part of it draggable:

<li ref={dragProvided.innerRef} {...dragProvided.draggableProps}>
  <span {...dragProvided.dragHandleProps} aria-label="Reorder">::</span>
  {task.title}
</li>

Give the handle an accessible name. It's a focusable control, and without one a screen reader announces nothing useful.

TypeScript Tips

DropResult, DraggableProvided and DroppableProvided are all exported. Typing the onDragEnd parameter as DropResult is usually the only annotation you need, since the render-prop arguments infer.

Common Gotchas

Unstable draggableId is the most common bug: using the array index means ids shift the moment something moves, and items teleport. Use a real id.

Second, React.StrictMode in development double-invokes effects and this library is sensitive to it. If dragging misbehaves only in dev, that's usually why.

Third, a scrollable ancestor with overflow: hidden clips the dragging item. The library lifts items with transforms, so the clip is real. Give the scroll container overflow: auto instead.

Frequently Asked Questions

Is this the same thing as react-beautiful-dnd?

It's a direct fork of it. Atlassian archived the original in 2023 and this fork picked it up, added React 18 and 19 support, moved the codebase to TypeScript and kept publishing. The component names and props are unchanged, which is why migrating is usually a dependency swap and a find-and-replace on the import path.

How do I migrate from react-beautiful-dnd?

Uninstall react-beautiful-dnd, install @hello-pangea/dnd, and replace the import specifier everywhere. The exported names are the same, so DragDropContext, Droppable and Draggable keep working. You can drop @types/react-beautiful-dnd at the same time, because types ship with the fork.

How should I choose between this and dnd-kit?

This one if you're dragging items within and between vertical or horizontal lists - kanban boards, reorderable settings, playlists. Its keyboard support and screen-reader announcements are the best in the category and they work with no configuration. dnd-kit if you need free 2D movement, custom collision detection, or dragging onto a canvas, which are the things a list-shaped library deliberately doesn't do.

Why is my list not reordering after a drop?

Because the library doesn't mutate your data. onDragEnd hands you source and destination indices and expects you to produce the new array and set state. If you don't, the item animates back. Also return early when destination is null, which is what a drop outside any droppable gives you.