Swipe actions ported
Reveal row actions with a swipe, or from the same actions in a menu.
A list whose rows reveal actions on a horizontal swipe, the way a mobile mail client does. Every row also includes an accessible More Actions menu powered by Bits UI DropdownMenu.
Loading demo…
Guidance
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 swipe gestures streamline high-volume item processing.
When not to use
- Use context-menu or dropdown-menu for desktop-first data tables.
- Use reorderable-list when rows need to be dragged vertically.
- Use hold-to-confirm for a single destructive action outside a list.
pnpm dlx shadcn-svelte@latest add @arcui/swipe-actionsUsage
<script lang="ts">
import { SwipeActions, SwipeActionsRow } from '$lib/components/swipe-actions';
import { Archive, MailOpen } from '@lucide/svelte';
let messages = $state([
{ id: '1', sender: 'GitHub', subject: 'New release v2.4.0 published', time: '10:42 AM' }
]);
function archive(id: string) {
messages = messages.filter(m => m.id !== id);
}
</script>
<SwipeActions label="Inbox">
{#each messages as message (message.id)}
<SwipeActionsRow
label={message.subject}
leading={[
{ label: 'Unread', icon: unreadIcon, tone: 'accent', keepRow: true, onSelect: () => {} }
]}
trailing={[
{ label: 'Archive', icon: archiveIcon, tone: 'danger', onSelect: () => archive(message.id) }
]}
>
<div class="message-preview">
<strong>{message.sender}</strong>
<span>{message.subject}</span>
</div>
</SwipeActionsRow>
{/each}
</SwipeActions>Variants & Examples
Dual Actions (Done & Archive)
Left swipe reveals Done (accent), right swipe reveals Archive (danger).
<SwipeActionsRow
label="Deploy update"
leading={[{ label: 'Done', tone: 'accent', onSelect: complete }]}
trailing={[{ label: 'Archive', tone: 'danger', onSelect: archive }]}
>
<RowItem />
</SwipeActionsRow>Keep Row After Action
Set keepRow: true for actions that toggle state without removing the row (such as Mark as Read).
<SwipeActionsRow
label="Message"
leading={[{ label: 'Unread', tone: 'accent', keepRow: true, onSelect: toggleUnread }]}
>
<MessageItem />
</SwipeActionsRow>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | – | Accessible name for the list, applied via aria-label on the root ul element. |
children | Snippet | – | SwipeActionsRow elements composing the list. |
leading | SwipeAction[] | [] | Actions placed under the left edge, revealed by swiping right. |
trailing | SwipeAction[] | [] | Actions placed under the right edge, revealed by swiping left. |
fullSwipe | boolean | true | Allows an extended swipe or flick to trigger the outermost action directly. |
class | string | – | Additional CSS class applied to the list container or row. |
Keyboard Interactions
| Key | Action |
|---|---|
Enter / Space | Opens the row More Actions menu from its trigger button. |
ArrowDown / ArrowUp | Navigates between actions in the dropdown menu. |
Escape | Closes the dropdown menu or resets any open swipe offset. |
Accessibility
- Semantic markup: list container is a ul with role="list" and aria-label; each row is an li.
- Every row includes an accessible "More actions for <label>" menu containing all actions.
- Revealed gesture buttons are aria-hidden and tab-index="-1", ensuring screen readers and keyboard users use the accessible dropdown.
- Focus is managed appropriately on item removal.
Motion
Phase 1 still-state: Row reveals leading/trailing action layers on horizontal pointer drag with 76px action snap points; dropdown menu features standard enter/exit animations. Phase 2 wires gesture velocity projection, rubber-band resistance beyond last stop, animated full-swipe stretch, and height collapse on row deletion.
Notes for AI
- Use for touch-first triage lists on mobile/touch interfaces.
- Every row requires a descriptive label for the accessible More Actions button.
- Handle state removal in onSelect; set keepRow: true if the item stays in the list.
Source of truth: registry/components/swipe-actions/swipe-actions.tsx