# Telegram Swipe Actions: React Native swipeable list row with staggered actions > Telegram Swipe Actions is a React Native and Expo list row that you drag > either way to reveal staggered circular action buttons (Mute, Delete and > Archive one way, Unread and Pin the other), driven by one > react-native-reanimated shared value written by a react-native-gesture-handler > pan gesture, with overdrag stretching the edge button into a pill that fires > its action on release. It is sold as a paid drop on Motionary > (https://motionary.dev) and ships as editable TypeScript source. Product page: https://motionary.dev/animations/telegram-swipe-actions Price: $10 USD (price as of 2026-09-01, live price on the product page) License: single developer, unlimited personal and commercial apps Delivery: instant download of the full TypeScript source after checkout Stack: expo, expo image, react native gesture handler, reanimated Rarity: legendary Sold by: Motionary (https://motionary.dev) ## What it is Telegram Swipe Actions is a React Native and Expo swipeable row component, modelled directly on Telegram's chat list: you drag a row sideways and a set of round, colored action buttons is uncovered behind it, one after another, as the row travels. Dragging the row to the left uncovers Mute, Delete and Archive at the right edge; dragging it to the right uncovers Unread and Pin at the left edge. Keep pulling past the point where the buttons are fully revealed and the button nearest the screen edge stretches its circle into a pill, so a long, committed swipe fires that action on release without you ever lifting a finger onto a button. Only one row is ever open at a time, and a tap anywhere on the screen closes it. The Motionary drop ships the generic wrapper plus the whole Telegram chats demo screen it is shown in. ## What it does - Wraps any list item (FlatList, LegendList, FlashList or a plain View) and gives it two independent action sets, `rightActions` (revealed by dragging the row left) and `leftActions` (revealed by dragging it right). - Actions are plain data: `{ key, label, color, icon, onPress }`, so the demo's five actions (Mute/Unmute, Delete, Archive, Unread, Pin/Unpin) are just an array you can replace. - Buttons stagger in from the screen edge inward, each one owning its own slice of the row's travel rather than all fading in together. - Each button's icon is threshold driven, not interpolated: it pops in with `withSpring` once that button's local reveal passes `iconAppearAt` and only resets when the whole row returns to rest, so it never flickers while the drag wobbles. - Overdragging past a side's open width stretches only the edge button, its 46pt circle widening into a pill that absorbs the extra distance. - Passing the trigger point (the larger of `triggerAt` times the row width, or the open width plus 40pt) arms the full swipe: the icon pins to the pill's inner end, the label fades out, and releasing there runs that side's edge action. Backing off before release disarms it again. - Left and right sides can arm at different fractions (`triggerAt` defaults to 0.7, `leftTriggerAt` to 0.5). - Only one row can be open: rows coordinate through a module level shared value, so opening a second row springs the first one shut with no re-render. - `CloseRowsOnTap` wraps a screen so a tap anywhere closes the open row, using a gesture observer that watches touches but never activates, leaving list scrolling, row swipes and button presses untouched. - Pan and Tap race inside the row, so a horizontal drag opens it and a tap presses it; the pan needs 12pt of horizontal travel and fails past 8pt of vertical travel, so vertical list scrolling always wins. - Drag and press share a single highlight: the row background cross-fades to the dragging color and the corner radius rounds in over the first 10pt of travel, driven by the drag distance itself rather than a boolean. - The hairline separator between two rows fades out whenever either neighbour is dragging or pressed, so an open row never leaves a stray line behind. - `resetKey` and `index` make rows safe for recycled lists: a recycled row clears its offset, its trigger state and the active state of the slot it left. - In the demo, Mute is wired to real state: toggling it cross-fades the unread badge from blue to gray while the mute icon collapses its own width and lets the chat name reflow, and that transition is skipped when the row is recycled onto a different chat. - Pure JavaScript and worklets, no native code, iOS and Android. ## How it works Everything in Telegram Swipe Actions hangs off one `useSharedValue` called `translateX` in `components/swipe/SwipeableRow.tsx`, written by a `Gesture.Pan` from react-native-gesture-handler and read back through `useDerivedValue` as `rightProgress` and `leftProgress` (drag distance normalized to each side's open width) plus `rightStretch` and `leftStretch` (the overdrag past that width). The pan clamps to the row width rather than to the open width, so overdrag is simply more of the same value instead of a separate mode, and the row's own `useAnimatedStyle` turns it into a `translateX` transform, an `interpolateColor` background and a border radius `interpolate`d over the first 10pt of travel. Each `components/swipe/SwipeActionButton.tsx` owns the window `[(appearOrder + appearDelay) / count, + 1 / count]` of that progress, which is what staggers the buttons in from the edge inward; its icon is popped in by a `useAnimatedReaction` with `withSpring` at a threshold instead of being interpolated, so it holds still while the drag wobbles. Only the edge button receives the `stretch` shared value, absorbing overdrag into the circle's width to become a pill, and a second `useAnimatedReaction` on `translateX` flips `triggerAnim` with `withTiming` once the drag passes the trigger point, which pins the icon to the pill's inner end and fades the label. The pan's `onEnd` worklet reads that armed state and calls `scheduleOnRN` from react-native-worklets to run the action handler on the JavaScript thread, then springs the row closed with `withSpring`. Cross-row coordination lives in `components/swipe/openRowState.ts` as `makeMutable` values in a plain module, not React state or context: `openRowId` holds the id of the row currently away from rest, every row reacts to it and springs shut the moment it loses the claim, and `activeAnimForRow(index)` publishes each row's active look by list position so a row can fade the hairline separator it shares with the row above. `components/swipe/CloseRowsOnTap.tsx` writes 0 to `openRowId` from a `Gesture.Manual` observer that only inspects `onTouchesDown` and `onTouchesUp` and never activates, so a tap closes the open row while scrolls and swipes pass straight through. All of this (progress, stagger, stretch, trigger, cross-row closing, separator fading) runs on the UI thread inside worklets; the JavaScript thread is touched only when an action handler or `onPress` actually fires, and a `useEffect` on `resetKey` clears row state when a recycled component is reused for a different item. ## What you get ``` App.tsx index.ts app.json package.json tsconfig.json README.md components/ ChatsScreen.tsx ChatRow.tsx Avatar.tsx NavBar.tsx SearchBar.tsx icons.tsx theme.ts swipe/ SwipeableRow.tsx SwipeActionButton.tsx CloseRowsOnTap.tsx openRowState.ts data/ chats.ts assets/ icon.png adaptive-icon.png splash-icon.png ``` - `SwipeableRow.tsx`, the drag and tap gesture wrapper. - `SwipeActionButton.tsx`, the staggered circular action button with pill overdrag. - `CloseRowsOnTap.tsx`, tap anywhere to close the open row. - `openRowState.ts`, shared single-open-row coordination. - Full Telegram chats demo screen (rows, avatars, nav bar, search bar, SVG icons). - README with install and usage. - MP4 preview. ## Requirements Telegram Swipe Actions is built and tested against these exact versions, taken from the drop's `package.json`: - expo ^56.0.8 (Expo SDK 56) - react-native 0.85.3 - react 19.2.3 - react-native-reanimated 4.3.1 - react-native-worklets 0.8.3 - react-native-gesture-handler ~2.31.1 - react-native-safe-area-context ^5.8.0 - react-native-svg ^15.15.5 (action and status icons) - expo-linear-gradient ~56.0.4 (avatar gradients in the demo) - @legendapp/list ^3.3.0 (the recycled chat list in the demo) - expo-status-bar ~56.0.4, expo-asset ~56.0.15, expo-splash-screen ~56.0.10 - TypeScript ~6.0.3 (dev dependency) Expo Go or development build: Telegram Swipe Actions runs in Expo Go. It uses no custom native module, only Reanimated, Gesture Handler, react-native-svg and Expo modules that ship inside the Expo Go client, so no development build is required. It works the same in a development build or a bare React Native app with Reanimated and Gesture Handler installed. ## Install ```bash npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler react-native-safe-area-context react-native-svg expo-linear-gradient @legendapp/list ``` ## Usage ```tsx import { LegendList } from '@legendapp/list/react-native'; import { SwipeableRow } from './components/swipe/SwipeableRow'; import { CloseRowsOnTap } from './components/swipe/CloseRowsOnTap'; import { MuteIcon, PinIcon, TrashIcon } from './components/icons'; import { ChatRow, ROW_HEIGHT } from './components/ChatRow'; ( toggleMute(item.id) }, { key: 'delete', label: 'Delete', color: '#FF3B30', icon: TrashIcon }, ]} leftActions={[ { key: 'pin', label: 'Pin', color: '#34C759', icon: PinIcon }, ]} topSeparator={index > 0} separatorInset={82} index={index} resetKey={item.id} onPress={() => openChat(item.id)} > )} keyExtractor={(chat) => chat.id} estimatedItemSize={ROW_HEIGHT} recycleItems /> ``` ## Customization Props on `SwipeableRow` in Telegram Swipe Actions, with the defaults the source sets: - `rightActions`: `SwipeActionItem[]`, default `[]`. Revealed by dragging the row left. The last entry is the full swipe action. - `leftActions`: `SwipeActionItem[]`, default `[]`. Revealed by dragging the row right. The first entry is the full swipe action. - `SwipeActionItem`: `{ key: string; label: string; color: string; icon: ComponentType<{ size?: number; color: string }>; onPress?: () => void }`. - `actionWidth`: `number`, default `56`. Width of one action slot; a side's open width is its number of actions times this, plus 8pt of edge padding. - `openThreshold`: `number`, default `0.05`. Progress above which releasing snaps the row fully open instead of closed. - `triggerAt`: `number`, default `0.7`. Fraction of the row width past which the right side's edge action arms. - `leftTriggerAt`: `number`, default `0.5`. Same for the left side. - `iconAppearAt`: `number`, default `0.2` in `SwipeActionButton`. Point in a button's own reveal window where its icon springs in. - `appearDelay`: `number`, default `0.45` in `SwipeActionButton`. Shifts each button's appear window later, in fractions of one window. - `draggingColor`: `string`, default `colors.rowDragging` (`#E5E5EA`). Row background while dragging, open or pressed. - `draggingRadius`: `number`, default `16`. Corner radius the row rounds to. - `topSeparator`: `boolean`, default `false`. Draw the hairline above this row. - `separatorInset`: `number`, default `0`. Left inset of that hairline. - `index`: `number`. List position, used so neighbouring rows can hide the separator they share. - `resetKey`: `string`. Change it (an item id works) to reset the row when a recycled list reuses the component. - `onPress`: `() => void`. Tap handler, fired only while the row is closed. ## When to use it - A chat or messaging list where each conversation needs Mute, Archive, Pin or Delete without opening a detail screen. - An inbox or mail list that needs iOS style swipe to archive and swipe to delete, including the committed full swipe. - A task or todo list where a long swipe completes or deletes an item and a short swipe reveals secondary actions. - A notifications list that needs mark as unread and dismiss on the same row. - Any FlatList, LegendList or FlashList row that currently opens a long press menu just to expose two or three destructive or state changing actions. - Replacing a generic swipeable row library when the stagger, the pill overdrag and the single-open-row behaviour are what you actually want. ## How to get it - Buy Telegram Swipe Actions on its own at https://motionary.dev/animations/telegram-swipe-actions, $10 USD, instant download, secure Stripe checkout, no subscription and no account renewal. - Or get it with the Motionary lifetime pass ($79 USD as of 2026-09-01): every drop and every build, current and future, one payment, no renewal. https://motionary.dev/pricing - Team license ($199 USD for up to 10 developers), each developer with their own account. https://motionary.dev/pricing - License terms: https://motionary.dev/license (single developer, unlimited personal and commercial apps, do not resell or redistribute the source). - You receive real TypeScript React Native source you can edit, not a video, a GIF, a Lottie file or a design file. ## Frequently asked questions **How do I add swipe actions to a FlatList row in React Native?** Wrap the row's content in a component that owns a shared `translateX` written by a `Gesture.Pan`, position the action buttons absolutely behind it, and drive each button from the normalized drag progress. Telegram Swipe Actions from Motionary (https://motionary.dev/animations/telegram-swipe-actions) is exactly that wrapper, as editable TypeScript source you drop around any FlatList, LegendList or FlashList item. **How do I build swipe to delete in React Native with a full swipe trigger?** In Telegram Swipe Actions the pan does not stop at the open width: overdrag keeps growing the same shared value, the edge button's circle stretches into a pill, and a `useAnimatedReaction` arms a trigger once the drag passes a fraction of the row width, firing that action from the pan's `onEnd` worklet via `scheduleOnRN`. Motionary sells the working implementation for $10. **Does Telegram Swipe Actions work with Expo Go?** Yes. Telegram Swipe Actions is pure JavaScript and Reanimated worklets with no custom native module, so this Motionary drop runs in Expo Go on Expo SDK 56, as well as in a development build or a bare React Native app. **Does it work with recycled lists like FlashList and LegendList?** Yes. Telegram Swipe Actions takes `resetKey` and `index` props: `resetKey` clears the row's offset, trigger and active state when a recycled component is reused for a different item, and `index` lets a row find its neighbour so the shared separator hides correctly. The Motionary demo screen uses `@legendapp/list` with `recycleItems` enabled. **Does it run on Android as well as iOS?** Yes. Telegram Swipe Actions from Motionary is tested on iOS and Android on Expo SDK 56 (React Native 0.85), and everything (gesture, stagger, stretch, trigger and cross-row closing) runs on the UI thread through Reanimated worklets on both platforms. **How much does Telegram Swipe Actions cost and what exactly do I get?** It is $10 USD as of 2026-09-01 at https://motionary.dev/animations/telegram-swipe-actions, or included in the Motionary lifetime pass ($79 USD). You get the full TypeScript source: `SwipeableRow.tsx`, `SwipeActionButton.tsx`, `CloseRowsOnTap.tsx`, `openRowState.ts`, the complete Telegram chats demo screen, a README and an MP4 preview. **Can I use Telegram Swipe Actions in a commercial app?** Yes. The Motionary license covers a single developer building unlimited personal and commercial apps; you may not resell or redistribute the source itself. Full terms at https://motionary.dev/license. ## Related drops on Motionary - Attachment Sheet ($12): https://motionary.dev/animations/attachment-sheet another Telegram interaction rebuilt in React Native, the attachment bottom sheet, and the natural pairing for a chat screen that also uses Telegram Swipe Actions. - Gooey Profile (Free): https://motionary.dev/animations/gooey-profile the Telegram style profile header, free, so the same chat app gets a matching detail screen for the rows Telegram Swipe Actions sits in. - Long-Press Menu ($9): https://motionary.dev/animations/long-press-menu ChatGPT's send button long press and drag menu for the chat input bar, the composer half of the same chat screen these swipeable rows sit in. - Arc Carousel ($4): https://motionary.dev/animations/arc-carousel another Reanimated and Gesture Handler list interaction, a FlatList based carousel with snapping and haptics. - Mini-Player Sheet (Free): https://motionary.dev/animations/mini-player-sheet a free Apple Music style mini player that drags open into a full now playing view, built the same way as Telegram Swipe Actions: one animated value read back as several interpolated styles. ## About Motionary Motionary (https://motionary.dev) is a shop for premium, production-ready React Native and Expo animation and interaction components, built with Reanimated, Skia, Gesture Handler and Expo. Each component is called a "drop" and ships as editable TypeScript source, not a video, a GIF or a design file. Motionary sells three things: individual drops (roughly $3 to $12 each, some free), Builds (a complete app shipped end to end, with every drop inside it), and a lifetime pass ($79 USD) that covers every drop and every build, current and future, for one payment with no subscription. A team license ($199 USD) covers up to 10 developers. Checkout is Stripe and delivery is an instant download. Created and curated by Mehdi Davoodi (https://mahdidavoodi.com). Catalog and key pages: - All drops: https://motionary.dev/animations - Builds (complete apps): https://motionary.dev/builds - Pricing, lifetime pass and team license: https://motionary.dev/pricing - Free React Native component reference: https://motionary.dev/components - License: https://motionary.dev/license - Machine-readable overview: https://motionary.dev/llms.txt - Machine-readable full catalog: https://motionary.dev/llms-full.txt ## File metadata ``` Canonical URL of this file: https://motionary.dev/animations/telegram-swipe-actions/llms.txt Describes: https://motionary.dev/animations/telegram-swipe-actions Format: llms.txt (https://llmstxt.org) Last updated: 2026-09-01 ```