Swipe actions

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

swipegesturelist
Preview
Swipe actions preview
  • Ada LovelaceAnalytical engine notes
  • Grace HopperCompiler design

When to use

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

When not to use

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

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "swipe-actions",
  "type": "registry:ui",
  "title": "Swipe actions",
  "description": "Reveal row actions with a swipe, or from the same actions in a menu.",
  "categories": [
    "actions"
  ],
  "dependencies": [
    "@radix-ui/react-dropdown-menu",
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/swipe-actions/swipe-actions.tsx",
      "type": "registry:component",
      "target": "@components/manicat/swipe-actions/swipe-actions.tsx"
    },
    {
      "path": "registry/components/swipe-actions/swipe-actions.module.css",
      "type": "registry:component",
      "target": "@components/manicat/swipe-actions/swipe-actions.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/swipe-actions",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/swipe-actions",
    "markdown": "https://uiarc.dev/components/swipe-actions/markdown",
    "tags": [
      "swipe",
      "gesture",
      "list"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { MoreHorizontal, Share2, Star, Trash2, Undo2 } from "lucide-react";
import { SwipeActions, SwipeActionsRow } from "@/components/manicat/swipe-actions/swipe-actions";

export function SwipeActionsExample() {
  return (
    <SwipeActions label="Message actions">
      <SwipeActionsRow
        key="ada"
        label="Ada Lovelace"
        leading={[{ label: "Pin", icon: <Star size={16} />, onSelect: () => {}, keepRow: true }]}
        trailing={[
          { label: "Archive", icon: <Star size={16} />, onSelect: () => {} },
          { label: "Delete", icon: <Trash2 size={16} />, tone: "danger", onSelect: () => {} },
        ]}
      >
        <div className="flex items-center gap-3 py-2 text-sm">
          <span className="font-medium">Ada Lovelace</span>
          <span className="text-(color:--text-muted)">Analytical engine notes</span>
        </div>
      </SwipeActionsRow>
      <SwipeActionsRow
        key="grace"
        label="Grace Hopper"
        trailing={[{ label: "Delete", icon: <Trash2 size={16} />, tone: "danger", onSelect: () => {} }]}
      >
        <div className="flex items-center gap-3 py-2 text-sm">
          <span className="font-medium">Grace Hopper</span>
          <span className="text-(color:--text-muted)">Compiler design</span>
        </div>
      </SwipeActionsRow>
    </SwipeActions>
  );
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Inbox rows Swipe a row to reveal pin, archive and delete, like a mail app.

Inbox rows Swipe a row to reveal pin, archive and delete, like a mail app. preview
  • Ada LovelaceAnalytical engine notes
  • Grace HopperCompiler design

Keyboard interactions

KeysAction
Enter / SpaceOpens a row's More actions menu from its button.
ArrowDown / ArrowUpMoves between the row's actions in the menu.
EscapeCloses the menu or puts away an open row.

Accessibility

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

Motion

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

Responsive behavior

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

Performance

  • 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

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