< all projects
Contact me

Full Swipe

React Native package

react-native-full-swipe

One swipe. Any action, on either side.

Most swipeable rows stop halfway and show a few buttons — the iOS Mail pattern. This is the other one, the pattern Telegram and Gmail use: the row doesn't stop. The action's colour fills the whole gap the row opens, and when you let go the action either happens or the row springs back.

npm install react-native-full-swipe
npx expo install react-native-reanimated react-native-gesture-handler
  • iOS
  • Android
  • Reanimated
  • Gesture Handler
  • Expo
  • TypeScript

See it move

  • 01demo
    Delete + undoLet go short and it springs back; past the line it goes.
  • 02demo
    Two sidesRight marks as read and stays, left archives and leaves.
  • 03demo
    ConfirmReturn false to keep the row.
  • 04demo
    Your own lookThe hook and backdrop, with your own content.

What it does

  • A line you can feel

    Past the threshold the icon jumps and onArmedChange fires — that is where the haptic goes. Back off and it jumps back.

  • A flick counts, a twitch doesn't

    A fast flick commits from a shorter swipe, but only once the row is a fair way over.

  • Each side has its own action

    One side can remove the row (delete, archive), the other can leave it in place (mark as read, pin).

  • Confirm, or fail

    Return false, a promise of it, or a rejected promise, and the row comes back. Works for confirm dialogs and failed network calls.

  • Undo included

    useUndo and UndoBar hold the last removed item for a few seconds.

  • On the UI thread

    Built on Reanimated and Gesture Handler, and every side is also a screen-reader action. No SVG or styling library.

Quick start

Quick start
import { SwipeRow, TrashIcon } from 'react-native-full-swipe';

<SwipeRow
  left={{ label: 'Delete', icon: <TrashIcon />, onAction: () => remove(item.id) }}
  right={{ label: 'Read', icon: <CheckIcon />, after: 'reset', onAction: () => markRead(item.id) }}
  onArmedChange={(armed) => armed && Haptics.impactAsync()}
>
  <MessageCard item={item} />
</SwipeRow>
Undo
const undo = useUndo<Drink>({ timeout: 5000 });

<SwipeRow
  left={{
    label: 'Delete',
    icon: <TrashIcon />,
    onAction: () => {
      undo.push(drink);
      setDrinks((list) => list.filter((d) => d.id !== drink.id));
    },
  }}
>
  <DrinkCard drink={drink} />
</SwipeRow>

<UndoBar
  visible={undo.item !== null}
  message={`${undo.item?.ml} ml removed`}
  onAction={() => {
    const back = undo.take();
    if (back) setDrinks((list) => [back, ...list]);
  }}
/>

Screenshots

  • A row swiped past the line, the trash icon popped
  • The undo bar after a row was removed
  • Swiping a row right to mark it read
  • Swiping a row left to archive it

More projects