Swipe actions

Reveal row actions with a swipe, or from the same actions in a menu.

pnpm dlx shadcn@latest add @uiarc/swipe-actions
Live · keyboard ready

Inbox

4 left to triage

  • Emma Collins9:41Atlas launch checklistQA signed off. Two billing blockers left before Thursday.
  • Marcus Johnson8:15Invoice INV-2048 from Northwind$4,280.00 is due on October 3. The PDF is attached.
  • Ava MitchellYesterdayResearch readout moved to FridayRoom 4B, 2:00 to 3:30. Interview clips are in the deck.
  • Sofia RamirezMonDesign crit notesNavigation feedback, with an owner for each follow-up.
  • 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-actions

Adds 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.