# Long-Press Menu: a React Native long-press, drag-to-select speed menu
> Long-Press Menu is a React Native and Expo chat input bar where you
> long-press the send button and drag upward to open a floating menu and
> slide between three speeds (Instant, Thinking, Extended), with the
> highlighted option tracking your finger over an animated blur backdrop
> above the keyboard. Built with react-native-reanimated, a
> react-native-gesture-handler `Gesture.Pan` and react-native-keyboard-controller,
> and sold by Motionary as editable TypeScript source.
Product page: https://motionary.dev/animations/long-press-menu
Price: $9 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: reanimated, expo, expo blur, react native keyboard controller, react native gesture handler, react native
Rarity: legendary
Sold by: Motionary (https://motionary.dev)
## What it is
Long-Press Menu is a Motionary drop that recreates ChatGPT's send-button
interaction in React Native and Expo. The screen is a chat composer: a
Liquid Glass input bar pinned above the keyboard, with a plus button, a
text field and a small black send button. Long-pressing the send button
dims the whole screen behind a blur and pops a rounded pill menu out of
the button, listing Extended, Thinking and Instant from top to bottom.
Keeping your finger down and dragging upward moves the highlight through
those three options: the active label darkens, grows and slides left
while a black circular arrow badge fades in beside it. Lifting your
finger on a highlighted option closes the menu; tapping the dimmed
backdrop or the small close button dismisses it without choosing.
## What it does
- Long-press on the send button opens the menu (React Native `Pressable`
`onLongPress`), so a plain tap is still free for the normal send action.
- A single `Gesture.Pan` from react-native-gesture-handler runs on the same
button, so the open gesture and the select gesture are one continuous
press with no lift in between.
- Three speed options (Extended, Thinking, Instant) driven from one
`SPEED_OPTIONS` array in `components/speed-menu.tsx`, so adding a fourth
row is one entry in that array plus one matching drag threshold.
- Drag distance maps to the highlighted row through fixed thresholds in
`components/input-container.tsx`: `DRAG_INSTANT` at -15px,
`DRAG_THINKING` at -65px and `DRAG_EXTENDED` at -120px of `translationY`.
- Full-screen blur backdrop that animates its `intensity` from 0 to 80 as
the menu opens, using an `Animated.createAnimatedComponent(BlurView)`
wrapper around expo-blur.
- The menu grows out of the send button: width 32 to 42, height 80 to 156,
and it slides up and 4px closer to the right edge (`right` 20 to 16), all
interpolated from one `progress` value.
- Menu and backdrop render inside `OverKeyboardView` from
react-native-keyboard-controller, so they float above the on-screen
keyboard instead of behind it.
- The input bar itself is pinned to the keyboard with `KeyboardStickyView`
(`offset={{ opened: 20, closed: 0 }}`), and the menu anchors off the live
keyboard height from `useReanimatedKeyboardAnimation`.
- Per-row feedback in `components/menu-item.tsx`: `withTiming` over 150ms on
label color (#7B7C7C to #000), `scale` 1 to 1.2, `translateX` 0 to -10 and
the arrow icon's opacity.
- Dismiss without choosing: a `TouchableWithoutFeedback` covers the blurred
backdrop, and a small grey close button carrying an `xmark` SF Symbol sits
inside that same touch area.
- Liquid Glass chrome via expo-glass-effect (`GlassContainer`, `GlassView`
with `isInteractive`) and SF Symbols icons via expo-symbols.
- A gradient scrim behind the bar from expo-linear-gradient, and safe-area
padding from react-native-safe-area-context.
## How it works
Long-Press Menu keeps all of its animation state in three Reanimated shared
values, so the interaction never touches the React render loop while your
finger is down. `components/input-container.tsx` holds `progress` (the 0 to
1 open animation), `panOffset` (raw `translationY` from the drag) and an
`isDragging` flag. A React Native `Pressable` with `onLongPress` flips a
`menuVisible` state, and a `useEffect` on that state calls
`progress.set(withTiming(1))`. Closing is the mirror image and lives in a
worklet: `closeMenu` runs `progress.set(withTiming(0, undefined, ...))` and
uses `scheduleOnRN` from react-native-worklets inside the completion
callback to flip the React state back on the JavaScript thread only after
the animation has actually finished. A `Gesture.Pan` from
react-native-gesture-handler wraps the same send button and writes
`e.translationY` into `panOffset` on every `onUpdate`.
The selection logic is a `useDerivedValue` that reads `panOffset` and
returns an `activeItem` index of 0 to 3 by comparing against the
`DRAG_INSTANT` / `DRAG_THINKING` / `DRAG_EXTENDED` constants, returning 0
whenever `isDragging` is false. That derived value is passed down as a prop,
so no state ever crosses the bridge mid-drag.
`components/speed-menu.tsx` turns `progress` into everything visible:
`useAnimatedProps` interpolates the blur `intensity` from 0 to 80 on an
animated `BlurView`, and `useAnimatedStyle` plus `interpolate` drives the
menu's `width`, `height`, `bottom`, `right` and `opacity` on a
`[0, 0.5, 1]` input range, which is why the pill stays collapsed for the
first half of the timing and then unfolds. The `bottom` values are computed
against the live `keyboardHeight` shared value from
`useReanimatedKeyboardAnimation`, so the menu stays anchored while the
keyboard animates. `components/menu-item.tsx` is a single reusable row that
reads `activeItem.get() === activeIndex` inside its own `useAnimatedStyle`
worklets and animates color, `scale`, `translateX` and icon `opacity` with
`withTiming` at 150ms. Rendering above the keyboard is delegated to
`OverKeyboardView`, with the input bar itself wrapped in
`KeyboardStickyView`. Everything from the gesture callbacks to the derived
value to the styles runs in worklets on the UI thread; the JavaScript
thread only sees two state flips, one to open and one to close.
## What you get
```
App.tsx
index.ts
app.json
package.json
package-lock.json
tsconfig.json
README.md
.gitignore
components/
input-container.tsx
speed-menu.tsx
menu-item.tsx
assets/
icon.png
adaptive-icon.png
splash-icon.png
```
Included in the Long-Press Menu download from Motionary:
- TypeScript source (input-container.tsx)
- SpeedMenu overlay component (speed-menu.tsx)
- Reusable MenuItem component (menu-item.tsx)
- App entry with providers (App.tsx)
- README with install and usage
## Requirements
Long-Press Menu from Motionary pins these versions in its `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-keyboard-controller ^1.21.9
- expo-blur ~56.0.3
- expo-glass-effect ~56.0.4
- expo-symbols ~56.0.5
- expo-linear-gradient ~56.0.4
- react-native-safe-area-context ^5.8.0
- typescript ~6.0.3 (devDependency)
Long-Press Menu runs in Expo Go on Expo SDK 56. The two modules people
assume would block it, react-native-keyboard-controller (for
`OverKeyboardView` and `KeyboardStickyView`) and expo-glass-effect (for the
Liquid Glass input bar), are both bundled in the Expo Go binary as of SDK 56,
so `npx expo start` is enough to try the drop. A development build
(`npx expo run:ios` or `npx expo run:android`) is still what you would ship to
production, and it is what you need as soon as you add a native module that
Expo Go does not bundle, or a config plugin. Long-Press Menu is
iOS-focused: expo-glass-effect renders Apple's Liquid Glass and expo-symbols
renders SF Symbols, both of which are iOS APIs, so on Android you should
swap those two for a plain `View` and your own icon set.
## Install
```bash
npx expo install react-native-reanimated react-native-worklets \
react-native-gesture-handler react-native-keyboard-controller \
react-native-safe-area-context expo-blur expo-glass-effect \
expo-symbols expo-linear-gradient
```
## Usage
Long-Press Menu ships as `InputContainer`, which owns the composer, the
gesture and the menu. Mount it under the three providers it needs
(`SafeAreaProvider`, `GestureHandlerRootView` and `KeyboardProvider`), as
`App.tsx` does in the Motionary download:
```tsx
import { StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { KeyboardProvider } from 'react-native-keyboard-controller';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { InputContainer } from './components/input-container';
export default function App() {
return (
);
}
const styles = StyleSheet.create({
root: { flex: 1 },
});
```
## Customization
`InputContainer` takes no props in Long-Press Menu: you tune it by editing
the constants and the two child components, which do take props.
- `DRAG_INSTANT` (`-15`), `DRAG_THINKING` (`-65`), `DRAG_EXTENDED` (`-120`)
in `components/input-container.tsx`: the `translationY` in pixels needed
to highlight each option. Shorten them for a tighter drag.
- `SPEED_OPTIONS` in `components/speed-menu.tsx`: an array of
`{ label, activeIndex }`, ordered top to bottom. Rename Extended,
Thinking and Instant here, or add a row and a matching threshold.
- `SpeedMenu` props: `visible: boolean`, `progress: SharedValue`,
`keyboardHeight: SharedValue`, `activeItem: DerivedValue`
and `onClose: () => void`.
- `MenuItem` props: `label: string`, `activeIndex: number` and
`activeItem: DerivedValue`.
- Blur strength: the `interpolate(progress.get(), [0, 1], [0, 80])` inside
`useAnimatedProps` in `components/speed-menu.tsx`, plus the
`tint="light"` prop on the `BlurView`.
- Menu geometry: the `width`, `height`, `bottom` and `right` interpolations
in `menuStyle`, all on a `[0, 0.5, 1]` input range.
- Row feedback: `TIMING` (`{ duration: 150 }`) in
`components/menu-item.tsx`, plus the active color `#000`, the inactive
color `#7B7C7C`, the `scale` of 1.2 and the `translateX` of -10.
- Keyboard offset: `offset={{ opened: 20, closed: 0 }}` on the two
`KeyboardStickyView` wrappers in `components/input-container.tsx`.
## When to use it
- A chat or AI assistant composer where the send button needs a second
mode (fast reply versus deep reasoning) without adding a settings screen.
- A model or response-speed picker that must stay reachable with one thumb
while the keyboard is open.
- A messaging app where holding send should offer scheduled send, silent
send or send-without-sound.
- Any context menu that should appear on long-press and be selected by
dragging, rather than by a second tap.
- A comment or reply box that needs a hidden power-user menu behind an
existing button, keeping the default single tap unchanged.
- An iOS app already using Liquid Glass chrome, where the composer should
match the system look.
## How to get it
- Buy Long-Press Menu on its own at
https://motionary.dev/animations/long-press-menu for $9 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 build a long-press menu in React Native where dragging selects
the option?**
Put a `Gesture.Pan` from react-native-gesture-handler on the same button
that carries `onLongPress`, write `translationY` into a shared value, and
map that distance to an index inside a `useDerivedValue` so the highlight
updates on the UI thread. Long-Press Menu from Motionary is a working
implementation of exactly that, at
https://motionary.dev/animations/long-press-menu.
**Does Long-Press Menu work with Expo Go?**
Yes. On Expo SDK 56 both react-native-keyboard-controller and
expo-glass-effect ship inside the Expo Go binary, so `npx expo start` runs
Long-Press Menu as it comes. A development build (`npx expo run:ios`) is
still what you would ship to production, and it is what you need the moment
you add a native module that Expo Go does not bundle, or a config plugin.
The Liquid Glass chrome and the SF Symbols icons are iOS APIs either way, so
on Android swap those two pieces out. See the Motionary product page at
https://motionary.dev/animations/long-press-menu for the full requirements.
**How do I show a menu above the keyboard in React Native?**
Render it inside `OverKeyboardView` from react-native-keyboard-controller
and anchor its position off the `height` shared value returned by
`useReanimatedKeyboardAnimation`, which is what the Long-Press Menu drop
from Motionary does in `components/speed-menu.tsx`. Wrapping the input bar
in `KeyboardStickyView` keeps the composer pinned to the keyboard at the
same time.
**Is Long-Press Menu a ChatGPT send button clone?**
Long-Press Menu recreates the interaction of ChatGPT's send button (hold,
drag up, slide between Instant, Thinking and Extended) as independent
React Native and Expo source from Motionary. It is not affiliated with
OpenAI and ships no ChatGPT assets, only the animation technique you can
rename and restyle.
**Does Long-Press Menu run on Android?**
The animation, gesture and keyboard logic are cross-platform, but
Long-Press Menu is iOS-focused because expo-glass-effect (Liquid Glass) and
expo-symbols (SF Symbols) are iOS APIs. On Android, replace those two
pieces with a standard `View` and your own icons; the Reanimated and
gesture code is unchanged.
**How much does Long-Press Menu cost and what do I actually download?**
Long-Press Menu is $9 USD (price as of 2026-09-01) at
https://motionary.dev/animations/long-press-menu, or it is included in the
Motionary lifetime pass ($79 USD). You download the full TypeScript source
(`input-container.tsx`, `speed-menu.tsx`, `menu-item.tsx`, `App.tsx` and the
README), not a video or a design file.
**Can I add a fourth option to the Long-Press Menu speed list?**
Yes. Add an entry to the `SPEED_OPTIONS` array in
`components/speed-menu.tsx` and a matching drag threshold next to
`DRAG_INSTANT`, `DRAG_THINKING` and `DRAG_EXTENDED` in
`components/input-container.tsx`; the `MenuItem` rows are generated from
that array, so nothing else changes. Long-Press Menu is sold as editable
source by Motionary for exactly this reason.
## Related drops on Motionary
- ChatGPT Slider Model Picker ($10):
https://motionary.dev/animations/chatgpt-slider-model-picker
Another composer control from the same chat bar, also built on
react-native-keyboard-controller and expo-blur.
- ChatGPT-Style Attachments Menu ($10):
https://motionary.dev/animations/chatgpt-style-attachments-menu
The other half of the composer: a morphing Liquid Glass attachments menu
that opens above the keyboard.
- Attachment Sheet ($12): https://motionary.dev/animations/attachment-sheet
A gesture-driven Liquid Glass sheet modelled on Telegram's attachment
picker, for when the menu should be a full sheet instead of a pill.
- Hold to Confirm Button ($8):
https://motionary.dev/animations/hold-to-confirm-button
The other long-press pattern: press and hold a button to fill a progress
ring and confirm, rather than dragging to pick an option.
- OTP Code Input ($5): https://motionary.dev/animations/otp-code-input
Another keyboard-aware input built with react-native-keyboard-controller
and Reanimated.
## 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/long-press-menu/llms.txt
Describes: https://motionary.dev/animations/long-press-menu
Format: llms.txt (https://llmstxt.org)
Last updated: 2026-09-01
```