●RORKMAX — Rork Max generates pure Swift instead of React Native, enabling true native apps across iPhone, iPad, Watch, TV, Vision Pro, and iMessage●APPLE — Rork's 2026 direction has a clear theme of native empowerment across the Apple ecosystem●EXPO — Standard builds run on React Native and Expo, so you're left with a real project structure and code you can keep working on●FUNDING — Rork recently raised $15M and now sees over 743,000 monthly visits with 85% growth●PRICING — Rork is free to start, with paid plans from $25/month and Rork Max at $200/month●CROSS — Rork builds iOS, Android, and web from a single prompt, finished off with a bit of follow-up tweaking●RORKMAX — Rork Max generates pure Swift instead of React Native, enabling true native apps across iPhone, iPad, Watch, TV, Vision Pro, and iMessage●APPLE — Rork's 2026 direction has a clear theme of native empowerment across the Apple ecosystem●EXPO — Standard builds run on React Native and Expo, so you're left with a real project structure and code you can keep working on●FUNDING — Rork recently raised $15M and now sees over 743,000 monthly visits with 85% growth●PRICING — Rork is free to start, with paid plans from $25/month and Rork Max at $200/month●CROSS — Rork builds iOS, Android, and web from a single prompt, finished off with a bit of follow-up tweaking
Long-Press Context Menus for a Gallery Item in a Rork Expo App
Long-pressing a wallpaper card does nothing, yet iOS users expect a preview and a menu. From why Pressable alone falls short, to a native context menu with zeego, resolving the scroll-vs-long-press conflict, wiring up save and share, and a custom overlay fallback for Android — all with working code.
I was scrolling a wallpaper gallery on a real device and, almost absentmindedly, long-pressed a card. Nothing happened. Anyone used to an iPhone has muscle memory from the Photos app and the home screen: press and hold, and a preview with a menu should appear. Answering that expectation with silence makes the whole app feel a notch cheaper. Working on wallpaper apps as an indie developer, I have watched this small friction quietly cost engagement more than once.
The list screens Rork generates usually wrap a Pressable with nothing but a tap-to-navigate handler. Let us start there and work out why the long-press menu, specifically, needs one extra step.
What onLongPress leaves on the table
Pressable ships with onLongPress, so detecting the gesture itself is trivial.
But all this can raise is a sheet or modal you drew yourself. It is not the experience iOS shows by default — the target lifting gently, the background blurring, the preview appearing before you even release your finger. That lift and blur come from UIContextMenuInteraction, a native mechanism you cannot fully reproduce by stacking views from JavaScript.
So the decision comes down to this. If matching the behavior iOS users already know is the priority, bridge to a native context menu. If you want full control over the animation or a unified look on Android, build your own around onLongPress. For a wallpaper app that only needs the familiar trio of save, share, and favorite, the native route wins on both experience and effort.
Decide between two approaches first
Before writing anything, here is the trade-off on one page.
Concern
Native context menu (zeego)
Custom overlay (Reanimated)
iOS preview lift
Exactly as standard
Close, but hard to match fully
Consistent Android look
Follows the Material popup
Fully yours to design
Implementation effort
Low
High (gestures and geometry)
Dynamic menu items
Somewhat constrained
Flexible
For the gallery I wanted the iOS feel out of the box, so I built with zeego first and only pulled the pieces I cared about on Android into a custom path. zeego is a thin wrapper over the native context menu, and it falls back to a regular menu where the native one is unavailable. When in doubt, I would start from zeego.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Why Pressable's onLongPress alone never gives you the iOS preview-and-menu experience, and where the line is
✦Building a native context menu with zeego and resolving the scroll-vs-long-press conflict via delayLongPress
✦Bundling save, share, and favorite into one menu, and switching to a custom overlay on Android
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
The point that matters here: the preview is fed the high-resolution item.full, not the thumbnail. If a low-res image gets scaled up the moment the card lifts, the softness is glaring and the whole effect backfires. The preview only lives for the instant before your finger releases, so loading the large image at exactly that moment is a design that pays off.
Resolving the scroll-versus-long-press conflict
The first pitfall I hit in the masonry gallery was the menu opening mid-scroll. The instant a finger lands gets misread as a long press. Dull the detection too much, though, and it flips into "I am holding it and nothing happens."
The sweet spot was delaying the long-press decision just slightly beyond the default. Give the trigger wrapper a delayLongPress to separate it from the start of a scroll and avoid the misfire.
In my case around 400ms was the border between accidental fires and frustration. At 250ms it went off the moment a scroll began; past 600ms it felt sluggish. Pair that with a single light haptic the instant the long press commits — it becomes a small warning under the finger before the target lifts, and makes an unwanted gesture easy to cancel. I recommend one gentle pulse and no more.
import * as Haptics from "expo-haptics";// once, when the long press commitsHaptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
The trick with haptics is not to hammer them. A strong buzz every time the menu opens ends up feeling cheap. Once, and gently.
Making save and share actually work
A menu item is only finished when the side effect behind it is finished too, not just the label. Saving to Photos is the heart of a wallpaper app, so make the permission request and the save the shortest path possible.
import * as MediaLibrary from "expo-media-library";import * as FileSystem from "expo-file-system";import { Share } from "react-native";async function saveToPhotos(item) { const { status } = await MediaLibrary.requestPermissionsAsync(); if (status !== "granted") { // On denial, guide the user to Settings (never fail silently) return promptOpenSettings(); } const local = await FileSystem.downloadAsync( item.full, FileSystem.cacheDirectory + item.id + ".jpg" ); await MediaLibrary.saveToLibraryAsync(local.uri); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);}async function shareWallpaper(item) { await Share.share({ url: item.full, message: item.title });}
Nothing happening when a save is denied is the most disappointing outcome of all. If permission is missing, surface a path to the Settings app. If the save succeeds, return a success haptic. Adding just those two turns the menu from "pressable decoration" into a feature people actually use.
Switch to a custom overlay on Android
zeego shows a menu on Android too, but not the lifting preview iOS gives you. In a wallpaper app, seeing the image large is the value, so on Android I leaned into a custom overlay with its own preview. A long press lays down a translucent backdrop, enlarges the target in the center, and lines up the same three actions below.
import { Platform } from "react-native";import Animated, { FadeIn } from "react-native-reanimated";function GalleryItem(props) { if (Platform.OS === "ios") return <WallpaperCard {...props} />; // Android: open a custom overlay from onLongPress return ( <Pressable delayLongPress={400} onPress={props.onOpen} onLongPress={() => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); props.openOverlay(props.item); }} > <Image source={props.item.thumb} style={styles.card} recyclingKey={props.item.id} /> </Pressable> );}
Keep a single overlay at the top of the screen and swap the open item through state. Giving each card its own overlay collides with list virtualization and inflates memory. "One heavy decoration, outside the list" is the same instinct you use when building a masonry layout. For the column distribution and layout foundation, I wrote up the details in laying out variable-height images in columns.
Acceptance checks and wrap-up
Before calling it done, I verify three things on a real device. Fast, continuous scrolling never triggers the menu by accident. The high-resolution preview arrives in time and is not scaled up softly. And denying a save surfaces a path to Settings. When those three line up, the long press stops being a hidden convenience and becomes a natural gesture people discover by touch.
As a next step, try varying the menu items by state. Turn "Save" into "Saved" for a wallpaper already in the library; flip the star for a favorite. zeego builds its items from state, so once you get here the list itself grows quietly smarter.
It is a single long press, but meeting the expectation already in an iOS user's hands is a quiet, real kind of satisfaction. I hope this helps with your own implementation, and thank you for reading.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.