Swipe actions
Reveal row actions with a swipe, or from the same actions in a menu.
pnpm dlx shadcn@latest add @uiarc/swipe-actionsLive · keyboard ready
Inbox
4 left to triage
- Touch-first triage lists such as mail, notifications, or tasks.
- Rows with one or two quick actions per side, like Archive and Mark unread.
- Lists where a full swipe should commit the main action and the row should animate away.
- Use context-menu or dropdown-menu for desktop-first tables.
- Use reorderable-list when rows need to be dragged vertically.
- Use hold-to-confirm for a single destructive action outside a list.
Installation
Add Swipe actions with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/swipe-actionsAdds the component and its local dependencies, and installs @radix-ui/react-dropdown-menu, motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
example.tsx
import { SwipeActions, SwipeActionsRow } from "@/registry/components/swipe-actions/swipe-actions";import { Archive, MailOpen } from "lucide-react"; export function Inbox({ messages }: { messages: Message[] }) { return ( <SwipeActions label="Inbox"> {messages.map(message => ( <SwipeActionsRow key={message.id} label={message.subject} leading={[{ label: "Unread", icon: <MailOpen size={18} />, tone: "accent", keepRow: true, onSelect: () => markUnread(message.id) }]} trailing={[{ label: "Archive", icon: <Archive size={18} />, tone: "danger", onSelect: () => archive(message.id) }]} > <MessagePreview message={message} /> </SwipeActionsRow> ))} </SwipeActions> );}API reference
3 parts. The first is the root.
SwipeActions
The list. Keeps one row open at a time and closes it on an outside press or Escape.
PropTypeDefaultDescription
labelRequiredstring–Accessible name for the list.childrenRequiredReactNode–SwipeActionsRow elements. Removing one animates its height closed.classNamestring–Extra class on the surface.SwipeActionsRow
One row whose actions reveal under its left and right edges on a horizontal swipe.
PropTypeDefaultDescription
labelRequiredstring–Names the row's More actions button, for example the message subject.leadingSwipeAction[][]Actions under the left edge, revealed by swiping right. The first one commits on a full swipe.trailingSwipeAction[][]Actions under the right edge, revealed by swiping left. The last one commits on a full swipe.fullSwipebooleantrueLets a long swipe or flick commit the outermost action.childrenRequiredReactNode–Row content.classNamestring–Extra class on the row.SwipeAction
One action in leading or trailing.
PropTypeDefaultDescription
labelRequiredstring–Shown under the icon and in the menu.iconRequiredReactNode–Action icon.onSelectRequired() => void–Runs on full swipe, tap, or menu choice. Remove the item here unless keepRow is set.tone"neutral" | "accent" | "danger""neutral"Fill of the revealed action.keepRowboolean–Springs the row home after the action instead of sliding it away.- EnterorSpace
- Opens a row's More actions menu from its button.
- ArrowDownorArrowUp
- Moves between the row's actions in the menu.
- Escape
- Closes the menu or puts away an open row.
- The list is a ul with role="list" and aria-label; each row is an li.
- Every row has a "More actions for <label>" menu with the same actions, so the gesture is never required.
- Revealed action buttons are aria-hidden and out of the tab order; after a removing action, focus moves to a neighbouring row.
- The row tracks the finger, rubber-bands past its last stop, and settles on a spring that carries the release velocity; a flick projects its travel.
- Past the threshold the outer action stretches across the row, then the row slides out and the list closes the gap.
- Reduced motion jumps the row to its stops and fades rows in and out instead of animating height.
- Swipes lock to the horizontal axis after the first movement, so vertical page scrolling still works on touch.
- A full swipe commits past the larger of the open width plus 48px or 56% of the row, measured with a ResizeObserver.
- Every row also has a More actions menu, so mouse and keyboard users never need the gesture.
- Drag updates a motion value directly, so rows do not re-render while following the finger.
- Rows are not virtualized and each has its own ResizeObserver; paginate long lists.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use for short, triage-style lists on touch (mail, notifications, tasks). For desktop-first tables use context-menu or a row menu.
- Remove the item from state inside onSelect; set keepRow for actions that keep the item, such as mark as unread.
- Rows must be direct children of SwipeActions, keyed by item id, so removal and undo animate.
The full library index for assistants is at /llms.txt.