useSwipeActions
Pre-alphaThe registry and the CLI are not published yet.Roadmap

useSwipeActions

Reveal actions behind a row with a horizontal swipe, with an optional full swipe.

Draft

The API shown here is conceptual and may change.

The hook behind Swipe actions. That page explains the direction lock, the resistance, the release rules, the full swipe and accessibility. This one covers the API.

Installation

npx axiom add use-swipe-actions
pnpm dlx axiom add use-swipe-actions
yarn dlx axiom add use-swipe-actions
bun x axiom add use-swipe-actions

Installs: react-native-reanimated, react-native-gesture-handler.

Usage

SwipeableRow covers most lists. It calls the hook, draws the actions behind its child and adds the accessibilityActions.

Conceptual
import { SwipeableList, SwipeableRow } from "@/components/swipeable-row";

function Inbox() {
	return (
		<SwipeableList>
			<FlatList
				data={mails}
				renderItem={({ item }) => (
					<SwipeableRow
						leading={[
							{
								key: "read",
								icon: "mail",
								label: "Read",
								color: "info",
								onPress: () => toggleRead(item),
							},
						]}
						trailing={[
							{
								key: "flag",
								icon: "flag",
								label: "Flag",
								color: "warning",
								onPress: () => flag(item),
							},
							{
								key: "delete",
								icon: "trash",
								label: "Delete",
								color: "error",
								fullSwipe: true,
								onPress: () => remove(item),
							},
						]}
					>
						<Item onPress={() => open(item)}>…</Item>
					</SwipeableRow>
				)}
			/>
		</SwipeableList>
	);
}

SwipeableList holds the id of the open row, so opening one closes the other. It doesn’t render anything.

Custom actions

Call the hook yourself to draw the actions differently, for example icons that grow with the swipe.

Conceptual
function Row({ mail }: { mail: Mail }) {
	const swipe = useSwipeActions({
		leadingWidth: 80,
		trailingWidth: 160,
		fullSwipe: "trailing",
	});

	const content = useAnimatedStyle(() => ({
		transform: [{ translateX: swipe.translateX.value }],
	}));
	const icon = useAnimatedStyle(() => ({
		transform: [{ scale: 0.8 + swipe.progress.value * 0.4 }],
	}));

	return (
		<View>
			<TrailingActions
				style={icon}
				armed={swipe.armed}
				onPress={swipe.close}
			/>
			<GestureDetector gesture={swipe.gesture}>
				<Animated.View style={content}>…</Animated.View>
			</GestureDetector>
		</View>
	);
}

Closing from code

close() animates the row back. Call it after an action that keeps the row, or when the list starts scrolling:

Conceptual
<FlatList onScrollBeginDrag={swipeableList.closeAll}  />

Options

OptionTypeDefaultDescription
leadingWidthnumber0Width in pt of the leading actions. 0 turns the right swipe off.
trailingWidthnumber0Width in pt of the trailing actions. 0 turns the left swipe off.
resistancenumber60Maximum extra distance past the actions’ width. Ignored on a side with fullSwipe.
velocityThresholdnumber500Release speed in pt/s that opens or closes whatever the position.
fullSwipe'leading' | 'trailing' | falsefalseSide where a long swipe triggers the outermost action.
fullSwipeThresholdnumber0.55Fraction of the row width that arms the full swipe.
onFullSwipe(side: 'leading' | 'trailing') => voidCalled on release while armed, after the row has left the screen.
onArmedChange(armed: boolean) => voidCalled when the full swipe arms or disarms. Default: a light haptic.
enabledbooleantrueTurns the swipe off, for example in selection mode.

Return value

| Key | Type | Description | | ------------ | ----------------------------------------- | ---------------------------------------------------------------- | ---------- | --------------------------- | | translateX | SharedValue<number> | Row offset in pt. Negative reveals the trailing actions. | | progress | SharedValue<number> | | translateX | / rowWidth, from 0to1. | | open | 'leading' \| 'trailing' \| null | Open side. React state. | | armed | boolean | true while a full swipe would trigger on release. React state. | | close | () => void | Animates the row closed. | | openSide | (side: 'leading' \| 'trailing') => void | Opens a side from code, for a first-use hint. | | gesture | PanGesture | For a GestureDetector around the row content. |