npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-swipeable-actions

v0.1.2

Published

High-performance native swipeable row component for React Native using Expo Modules

Readme

react-native-swipeable-actions

A high-performance native swipeable row component for React Native. Native alternative to react-native-gesture-handler/Swipeable - no react-native-reanimated required.

Features

  • Pure native gestures - no JS thread blocking
  • 60fps spring animations on both iOS and Android
  • No react-native-reanimated dependency
  • Built-in FlashList/virtualized list support via recyclingKey
  • AutoClose pattern for chat-style swipe-to-reply
  • Lazy action rendering for optimal performance

Performance

Benchmark comparison on Android 16, Pixel 8 Pro, GrapheneOS — List Demo with 100 items:

https://github.com/user-attachments/assets/6d7cf1e6-c719-4044-8b47-205e94a38d10

https://github.com/user-attachments/assets/f59ef788-caf4-4985-93bc-8d098122fe1a

Videos not loading? View on GitHub

Installation

npm install react-native-swipeable-actions

Peer Dependencies

{
  "expo": ">=50.0.0",
  "expo-modules-core": ">=1.0.0",
  "react": ">=18.0.0",
  "react-native": ">=0.73.0"
}

Basic Usage

import { Swipeable, SwipeableMethods } from 'react-native-swipeable-actions'
import { useRef } from 'react'
import { View, Text, TouchableOpacity } from 'react-native'

function DeleteAction({ onPress }: { onPress: () => void }) {
  return (
    <TouchableOpacity onPress={onPress} style={styles.deleteAction}>
      <Text style={styles.actionText}>Delete</Text>
    </TouchableOpacity>
  )
}

function MyRow() {
  const swipeableRef = useRef<SwipeableMethods>(null)

  return (
    <Swipeable
      ref={swipeableRef}
      actions={<DeleteAction onPress={() => console.log('Delete!')} />}
      actionsWidth={80}
      onSwipeEnd={(state) => console.log('Swipe ended:', state)}
    >
      <View style={styles.row}>
        <Text>Swipe me left</Text>
      </View>
    </Swipeable>
  )
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | required | Row content | | actions | ReactNode | required | Action buttons revealed on swipe | | actionsWidth | number | required | Width of actions container in pixels | | actionsPosition | 'left' \| 'right' | 'right' | Position of actions ('left' = swipe right to reveal) | | friction | number | 1 | Drag damping factor (0-1). Lower = more resistance | | threshold | number | 0.4 | Snap-to threshold as percentage of actionsWidth (0-1) | | dragOffsetFromEdge | number | 0 | Minimum drag distance before gesture starts | | autoClose | boolean | false | Auto-close after swipe release (for swipe-to-reply) | | autoCloseTimeout | number | 0 | Delay in ms before auto-closing | | recyclingKey | string \| number | - | Unique key for FlashList/recycling support | | style | StyleProp<ViewStyle> | - | Container style | | testID | string | - | Test ID for e2e testing |

Callbacks

| Callback | Type | Description | |----------|------|-------------| | onSwipeStart | () => void | Called when swipe gesture begins | | onSwipeStateChange | (state: 'open' \| 'closed') => void | Called when gesture ends (before animation) | | onSwipeEnd | (state: 'open' \| 'closed') => void | Called when animation completes | | onProgress | (progress: number) => void | Called on each frame (0 = closed, 1 = fully open) |

Ref Methods

Access via ref:

const swipeableRef = useRef<SwipeableMethods>(null)

// Close the row (with optional animation)
swipeableRef.current?.close()         // animated
swipeableRef.current?.close(false)    // instant

// Open the row
swipeableRef.current?.open()

Static Methods

Control swipeables globally by their recyclingKey:

import { Swipeable } from 'react-native-swipeable-actions'

// Open a specific row
Swipeable.open('row-1')

// Close a specific row
Swipeable.close('row-1')           // animated
Swipeable.close('row-1', false)    // instant

// Close all open rows
Swipeable.closeAll()               // animated
Swipeable.closeAll(false)          // instant

AutoClose Pattern (Swipe-to-Reply)

For chat-style swipe-to-reply where the row should automatically close:

<Swipeable
  actions={<ReplyIndicator />}
  actionsWidth={60}
  actionsPosition="right"
  autoClose={true}
  threshold={0.6}
  onSwipeEnd={(state) => {
    if (state === 'open') {
      // Trigger reply action
      onReply(message)
    }
  }}
>
  <ChatMessage message={message} />
</Swipeable>

FlashList Integration

For virtualized lists, use recyclingKey to persist swipe state across recycling:

import { FlashList } from '@shopify/flash-list'

function MessageList({ messages }) {
  return (
    <FlashList
      data={messages}
      renderItem={({ item }) => (
        <Swipeable
          recyclingKey={item.id}
          actions={<DeleteAction />}
          actionsWidth={80}
        >
          <MessageRow message={item} />
        </Swipeable>
      )}
      estimatedItemSize={60}
    />
  )
}

Architecture

┌─────────────────────────────────────────────────────────────┐
│  JS Layer (React)                                           │
│  ┌─────────────────┐  ┌──────────────────────────────────┐ │
│  │ <Swipeable>     │  │ Actions (ReactNode)              │ │
│  │  - renderActions│──│  - Rendered lazily on first swipe│ │
│  │  - ref methods  │  │  - Any React components          │ │
│  └────────┬────────┘  └──────────────────────────────────┘ │
└───────────│─────────────────────────────────────────────────┘
            │ Expo Modules Bridge
┌───────────▼─────────────────────────────────────────────────┐
│  Native Layer (Kotlin/Swift)                                │
│  ┌─────────────────────────────────────────────────────────┐│
│  │ SwipeableView                                           ││
│  │  - UIPanGestureRecognizer (iOS) / OnTouchListener (And) ││
│  │  - Spring animations (UIView.animate / DynamicAnimation)││
│  │  - Progress events @ 60fps (CADisplayLink / Choreograph)││
│  │  - LRU cache for recycling state persistence            ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

Key design decisions:

  • Gestures handled entirely in native code for 60fps performance
  • Actions rendered lazily on first swipe gesture (performance optimization)
  • Recycling key enables state persistence in virtualized lists
  • No SharedValue dependency - progress synced via native events

Platform Support

| Platform | Version | |----------|---------| | iOS | 12.0+ | | Android | API 21+ |

TypeScript

Full TypeScript support with exported types:

import {
  Swipeable,
  SwipeableProps,
  SwipeableMethods,
  SwipeableStatic,
  SwipeProgressEvent,
  SwipeStateEvent,
} from 'react-native-swipeable-actions'

Testing

Jest mock is included for testing components that use Swipeable:

// jest.config.js
import 'react-native-swipeable-actions/jestSetup.js'

License

MIT