nalarx-ui
Components

Drag row

Drag to reorder a short list by a handle in the row's left gutter, on native HTML5 drag and drop.

bunx shadcn@latest add @nalarx/drag-row

Usage

useReorder holds the drag state for one list; reorderById computes the new order on drop. Leave gutter (22px) of room left of the list for the handle.

import { DragRow, reorderById, useReorder } from "@/components/ui/nx/DragRow";

const api = useReorder((fromId, toId) =>
  setItems((list) => reorderById(list, (item) => item.id, fromId, toId)),
);

<div className="pl-5.5">
  {items.map((item) => (
    <DragRow key={item.id} id={item.id} api={api} label={t("list.move", { name: item.name })}>
      <ItemRow item={item} />
    </DragRow>
  ))}
</div>

Only the handle starts a drag, so text and buttons inside a row keep working; the whole row is the drop target, outlined while a row hovers over it. The handle shows on hover (alwaysShowHandle keeps it visible) and drags a copy of the whole row.

Hover a row, then drag its handle:

Revenue
Orders
New customers
Refunds
Revenue
Orders
New customers
Refunds

Keyboard

There is no keyboard reordering, and no touch support: native HTML5 drag and drop works with a mouse or trackpad only. The handle is not focusable and is hidden from screen readers. Where keyboard or touch users must reorder, give them another way (move up and down buttons, or a position field).

Props

DragRow:

PropTypeDefault
idstringThe row's id, as reorderById reads it.
apiReorderApiFrom useReorder.
labelstringThe handle's tooltip.
gutternumber22Gutter width in px.
alwaysShowHandlebooleanfalseFor rows that carry other rows, such as a section header.

useReorder(onMove) returns { dragId, overId, handleProps(id), dropProps(id) } for building your own row. reorderById(items, idOf, fromId, toId) is pure: it returns a new array and leaves the order alone for unknown ids. setRowDragImage(event, row) sets a whole-row drag image.

Replaces

Web DragRow.tsx (DragRow, useReorder, reorderById, setRowDragImage, ReorderApi) and its test.

On this page