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

@curious-cat-consulting/react-native-swipeable-list

v0.1.0

Published

iOS 26 Mail-style swipeable list row for React Native and Expo

Readme

react-native-swipeable-list

A drop-in FlashList plus a coordinated SwipeableRow that gives you iOS Mail-style "only one row open at a time" behavior for free.

Install

npx expo install @curious-cat-consulting/react-native-swipeable-list

Then add the peer dependencies your app doesn't already have:

npx expo install @shopify/flash-list react-native-gesture-handler react-native-reanimated

This library also expects react, react-native, and react-native-worklets at the versions listed in package.json.

Quick start

Use SwipeableFlashList like FlashList. It wraps each item in a SwipeableRow for you — just pass renderTrailingActions (and/or renderLeadingActions):

import { Pressable, StyleSheet, Text, View } from 'react-native';
import { SwipeableFlashList } from '@curious-cat-consulting/react-native-swipeable-list';

type Item = { id: string; title: string };

const items: Item[] = [
  { id: '1', title: 'Inbox' },
  { id: '2', title: 'Sent' },
];

export function MailList() {
  return (
    <SwipeableFlashList
      data={items}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <View style={styles.row}>
          <Text>{item.title}</Text>
        </View>
      )}
      renderTrailingActions={({ item }) => (
        <Pressable style={styles.delete} onPress={() => console.log('delete', item.id)}>
          <Text style={styles.deleteLabel}>Delete</Text>
        </Pressable>
      )}
    />
  );
}

const styles = StyleSheet.create({
  row: { padding: 16, backgroundColor: '#fff' },
  delete: {
    backgroundColor: '#ff3b30',
    justifyContent: 'center',
    paddingHorizontal: 24,
  },
  deleteLabel: { color: '#fff', fontWeight: '600' },
});

That's the whole integration: SwipeableFlashList wraps a FlashList in a coordination provider and wraps each item in a SwipeableRow that registers with that provider, so opening one row closes the others. The action renderers receive the row's renderItem info first, so actions can vary per item.

Need finer control? Render SwipeableRow yourself inside renderItem instead of passing the action props.

Action buttons

Instead of hand-rolling the revealed buttons, drop in SwipeActionCircle — a 44pt circular button styled after iOS 26's Mail swipe actions, which scales and staggers into place as the row opens (no animation wiring needed):

import { SwipeableFlashList, SwipeActionCircle } from '@curious-cat-consulting/react-native-swipeable-list';

<SwipeableFlashList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={({ item }) => <Row item={item} />}
  renderTrailingActions={({ item }) => [
    <SwipeActionCircle
      icon="archivebox.fill"
      label="Archive"
      color="#34c759"
      onPress={() => archive(item.id)}
    />,
    <SwipeActionCircle icon="trash.fill" label="Delete" onPress={() => remove(item.id)} />,
  ]}
/>;

icon accepts an SF Symbol name (a string), a platform map ({ ios: 'square.and.arrow.up', android: 'share' } for cross-platform rendering via expo-symbols), or a render function ({ tintColor, size }) => ReactNode for a custom icon. Passing a render function means your app doesn't depend on expo-symbols at all:

import { Ionicons } from '@expo/vector-icons';

<SwipeActionCircle
  icon={({ tintColor, size }) => <Ionicons name="trash" color={tintColor} size={size} />}
  label="Delete"
  onPress={() => remove(item.id)}
/>;

Tapping a circle does what iOS does when you take a swipe action: a light haptic fires, your onPress runs, and the row closes. Both extras are on by default and independently opt-out-able — noHaptic to stay silent, noClose to leave the row open (e.g. an in-place toggle). The haptic uses expo-haptics, already a peer dependency.

See src/action/README.md for the full prop list.

Pull to commit (iOS full-swipe)

Drag a row far enough past open and a confirming haptic fires — the iOS "let go now" cue. Release there and the edge action (the outermost one) runs as if tapped; ease back under the line first and a lighter reset haptic says it won't. It's on automatically for any row with actions — nothing to wire up — and the commit point is tunable (commitTokens.threshold, default an 85%-of-open overdrag). See src/row/README.md.

What each piece does

| Component | Role | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SwipeableFlashList | Drop-in FlashList. Forwards every FlashList prop and its ref, auto-wraps each item in a SwipeableRow, and adds allowMultipleOpen plus renderLeadingActions/renderTrailingActions. | | SwipeableRow | Drop-in ReanimatedSwipeable. Forwards every swipeable prop. Participates in list coordination when inside a SwipeableFlashList. Use directly only when you need finer control than the list props give. |

SwipeableFlashListProps<T> is FlashListProps<T> plus:

| Prop | Default | Description | | ----------------------- | ------- | ------------------------------------------------------------------------ | | allowMultipleOpen | false | When false, opening a row closes any other open row. | | renderLeadingActions | — | Actions revealed by swiping a row from the leading edge (left in LTR). | | renderTrailingActions | — | Actions revealed by swiping a row from the trailing edge (right in LTR). |

SwipeableRowProps mirrors gesture-handler's SwipeableProps — see their ReanimatedSwipeable docs for thresholds, callbacks, and the rest — except renderLeftActions/renderRightActions are renamed to renderLeadingActions/renderTrailingActions.

Imperative control

SwipeableFlashList's ref is a normal FlashListRef<T>scrollToIndex, etc. work unchanged.

SwipeableRow's ref exposes a small handle for closing a row:

import { useRef } from 'react';
import { SwipeableRow, type SwipeableRowHandle } from '@curious-cat-consulting/react-native-swipeable-list';

const rowRef = useRef<SwipeableRowHandle>(null);
rowRef.current?.close();

Advanced: custom lists

If you aren't using FlashList, wrap your own list in SwipeableListProvider and render SwipeableRows inside it. See src/context/README.md.

SwipeableRow also works standalone outside any provider — it behaves exactly like ReanimatedSwipeable with no coordination.

Further reading