Drag Handle
- Stable
- WCAG 2.2 evidence
- RTL
Grip that starts a drag of the collection item it sits in.
Built on React Aria Button with slot="drag": inside a draggable GridList item, Enter starts a keyboard drag,
Tab or the arrow keys pick a drop position, Enter drops and Escape cancels, and every step is announced (WCAG 2.1.1,
4.1.2, 4.1.3). Grabbed is not a prop: the item being dragged carries data-dragging and the handle follows it. Dragging always needs a single-pointer alternative (WCAG 2.5.7): use it inside SortableItem / BoardCard, whose
lists offer Move up, Move down and Move to column actions, or provide equivalent actions yourself.
The 24 x 32 px box meets the 24 px minimum target size (WCAG 2.5.8).
import { DragHandle } from "@nexera-ui/react";- 3
- examples
- 18
- props
- 1
- live controls
- 1
- platform
- 9
- WCAG criteria
- 0
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { DragHandle } from "@nexera-ui/react";
<DragHandle />Examples 2
The same examples as Storybook, rendered live. Open Code to copy one.
In sortable list
The handle in a sortable list: Enter on the handle starts a keyboard drag; the Move menu is the single-pointer alternative.
import { useState } from "react";
import { SortableItem, SortableList } from "@nexera-ui/react";
const steps = [
{ id: "offer", title: "Sign offer letter", meta: "Day 1 · HR" },
{ id: "laptop", title: "Collect laptop and badge", meta: "Day 1 · IT" },
{ id: "buddy", title: "Coffee with your onboarding buddy", meta: "Day 2 · Team" },
];
function ListDemo() {
const [items, setItems] = useState(steps);
return (
<SortableList
aria-label="Onboarding checklist"
items={items}
onReorder={setItems}
className="max-w-[27.5rem]"
>
{(step) => <SortableItem label={step.title} meta={step.meta} />}
</SortableList>
);
}
export function InSortableList() {
return <ListDemo />;
}
In kanban board
The handle on Kanban cards: drag across columns, or use Move to column in the card's menu.
import { useState } from "react";
import { Board, BoardCard, BoardColumn } from "@nexera-ui/react";
interface Reviewee {
id: string;
name: string;
role: string;
}
const columns: BoardColumnData<Reviewee>[] = [
{
id: "todo",
title: "To review",
items: [
{ id: "zara", name: "Zara Siddiqui", role: "Frontend engineer" },
{ id: "omar", name: "Omar Farooq", role: "Data analyst" },
],
},
{ id: "done", title: "Reviewed", items: [] },
];
function BoardDemo() {
const [value, setValue] = useState(columns);
return (
<Board columns={value} onColumnsChange={setValue}>
{value.map((column) => (
<section key={column.id} className="flex w-60 flex-col gap-2 rounded-lg bg-sunken p-2">
<h3 id={`handle-${column.id}`} className="m-0 px-1 text-heading-h4 text-primary">
{column.title}
</h3>
<BoardColumn<Reviewee> columnId={column.id} aria-labelledby={`handle-${column.id}`}>
{(card) => <BoardCard name={card.name} subtitle={card.role} />}
</BoardColumn>
</section>
))}
</Board>
);
}
export function InKanbanBoard() {
return <BoardDemo />;
}
Props 18
Press "Try it" on a card to load that prop into the playground.
18 props shown
aria-labelNexerastringAccessible name. Leave it unset inside a draggable GridList / ListBox item: React Aria then names the handle
"Drag <item text>" in the provider locale. Set it only when the handle is used outside such an item.
slotNexerastring | nullReact Aria slot. "drag" connects the handle to the enclosing draggable collection item, which supplies the
keyboard and screen reader drag behaviour, the name and the disabled state.
classNameNexerastringExtra classes, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style.
isDisabledReact AriabooleanWhether the button is disabled.
onPressReact Aria(e: PressEvent) => voidHandler that is called when the press is released over the target.
onPressStartReact Aria(e: PressEvent) => voidHandler that is called when a press interaction starts.
onPressEndReact Aria(e: PressEvent) => voidHandler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChangeReact Aria(isPressed: boolean) => voidHandler that is called when the press state changes.
onPressUpReact Aria(e: PressEvent) => voidHandler that is called when a press is released over the target, regardless of whether it started on the target or not.
autoFocusReact AriabooleanWhether the element should receive focus on render.
typeReact Aria"button" | "submit" | "reset"The behavior of the button when used in an HTML form.
formReact AriastringThe <form> element to associate the button with.
The value of this attribute must be the id of a <form> in the same document.
See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#form).
nameReact AriastringSubmitted as a pair with the button's value as part of the form data.
valueReact AriastringThe value associated with the button's name when it's submitted with the form data.
idReact AriastringThe element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
aria-labelledbyReact AriastringIdentifies the element (or elements) that labels the current element.
aria-describedbyReact AriastringIdentifies the element (or elements) that describes the object.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
7 direct · 2 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.11Non-text ContrastLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · tested directly
- 2.5.7Dragging MovementsLevel AA · tested directly
- 2.5.8Target Size (Minimum)Level AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-disableddata-draggingdata-hovereddata-pressed
<DragHandle className="data-disabled:opacity-90 shadow-sm" />Used in blocks
No block uses DragHandle yet.
Related components
- BoardCardA Kanban card: avatar, name, role, drag handle and a detail line.
- ButtonTriggers an action such as Save, Submit, Approve or Delete.
- ButtonGroupItemOne toggle in a joined row of buttons, for example a List / Grid view switch or a set of filters.
- ContextMenuThe menu of actions for an element, opened where the user asks for it: right-click, long-press on touch screens, and from the keyboard with Shift+F10 or the ContextMenu key (the browser reports both as a `contextmenu` event on the focused element; on macOS, Control+Enter).
- DropIndicatorInsertion line shown between two items while something is dragged over a `GridList` or `ListBox`.
- IconButtonA square, icon-only button for compact actions such as Settings, Close or Delete row.
- MenuA list of actions or options that opens from a trigger.
- MenuDividerSeparates groups of items in a `Menu` or `ContextMenu`.