# ChatGPT-Style Attachments Menu: React Native morphing chat attachments menu
> ChatGPT-Style Attachments Menu is a React Native and Expo component in which
> a chat input bar's plus button morphs into a floating Liquid Glass
> attachments panel that extends full screen into a recycled photo grid or a
> live camera, driven by two react-native-reanimated spring values whose
> geometry is one continuous blend of the closed, open and extended states.
> It is sold by Motionary as editable TypeScript source.
Product page: https://motionary.dev/animations/chatgpt-style-attachments-menu
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, react native, react native keyboard controller, reanimated
Rarity: legendary
Sold by: Motionary (https://motionary.dev)
## What it is
ChatGPT-Style Attachments Menu is a React Native and Expo attachments flow for
a chat composer, modelled on the plus button in ChatGPT's iOS input bar. Tap
the plus and the 46 by 46 pill grows upward and outward into a 270 by 280
Liquid Glass panel holding four options (Camera, Photos, Files, Plugins), with
the plus icon fading out as the panel's glass material fades in. Tap Photos or
Camera and the same panel keeps growing until it fills the screen width,
becoming a three column scrolling photo grid or a live camera preview with a
shutter button. Tapping outside the panel, on a full screen transparent
backdrop, collapses it straight back down into the plus in one continuous
motion, with no intermediate step back through the menu items and no snap.
This drop is sold by Motionary.
## What it does
- Morphs a chat input bar's plus button into a floating attachments panel and
back, as a single continuous animation rather than two separate transitions.
- Blends three key geometry states (closed, open, extended) so the panel can
collapse directly from the full screen camera or photo view to the plus.
- Extends into two surfaces: a recycled three column photo grid rendered with
`@legendapp/list`, and a live `expo-camera` preview with a shutter control.
- Renders the panel on iOS Liquid Glass via `expo-glass-effect`, with
`glassEffectStyle` and `tintColor` animated in as the menu opens, and the
extended surface's back and All Photos controls on static `regular` glass.
- Anchors above the software keyboard: the overlay lives in
`OverKeyboardView` and its bottom, left and height all read live keyboard
height from `useReanimatedKeyboardAnimation`.
- Keeps the input bar itself glued to the keyboard with `KeyboardStickyView`,
and tightens the bar's horizontal padding from 32 to 12 as the keyboard rises.
- Mounts the extended surface lazily, so the 20 grid images are not fetched
until you actually extend and the menu items stay tappable before that.
- Handles interrupted animations: re-pressing the plus during the closing
spring's settle tail reopens the menu instead of being a dead press, because
the overlay only unmounts when the spring reports `finished`.
- Requests camera permission on demand with `useCameraPermissions` when you
choose the Camera option.
- Four menu items rendered with SF Symbols through `expo-symbols` (`camera`,
`photo`, `paperclip`, `puzzlepiece.extension`).
- Ships an in-code note on three iOS Liquid Glass quirks it works around:
glass views do not nest, animating opacity on a glass ancestor suppresses
the material, and SwiftUI backed buttons do not render inside
`OverKeyboardView`.
## How it works
ChatGPT-Style Attachments Menu runs the whole morph off two
react-native-reanimated shared values held in `components/input-container.tsx`:
`progress` (0 to 1) opens the plus into the menu, and `extendProgress` (0 to 1)
grows that menu into the full screen surface. Both are set with `withSpring`
(duration 450); there is no pan gesture, presses simply set the targets, so
every frame is computed on the UI thread with no bridge traffic. Instead of
branching on which state the menu is in,
`components/attachments-menu.tsx` treats each geometry channel as one
continuous three state blend: every `useDerivedValue` computes
`closed + (open - closed) * progress + (extended - open) * extendProgress` for
`bottom`, `left`, `width` and `height`, which means the box is defined
everywhere between the three keyframes. Because it is continuous, dropping both
drivers to zero with the same spring shrinks the panel straight from the
extended surface back to the plus, which is what removes the snap you get from
sequencing two animations. A single `useAnimatedStyle` reads those four derived
values and also interpolates a `boxShadow` string from `progress`. The Liquid
Glass surface is `Animated.createAnimatedComponent(GlassView)` whose
`useAnimatedProps` flips `glassEffectStyle` from `'none'` to `'clear'` past
`progress > 0.01` and runs `interpolateColor` on `tintColor`, while the menu
items use their own opacity worklet, `Math.max(0, openFade -
extendProgress.get())`, so they fade in on open and stay hidden during a direct
close. Because animating opacity on an ancestor of a glass view kills the
material (expo/expo issue 41024), the extended surface's back and All Photos
buttons are scaled and translated instead of faded, and they are mounted as
siblings of the menu's glass rather than children of it. The overlay is an
`OverKeyboardView` from react-native-keyboard-controller containing a
`GestureHandlerRootView`, an absolutely filled `Pressable` backdrop that calls
the close worklet, and the animated glass panel; `scheduleOnRN` from
react-native-worklets hops back to JavaScript to unmount state only once a
spring's `finished` callback is true.
## What you get
```
App.tsx
index.ts
app.json
package.json
package-lock.json
tsconfig.json
README.md
.gitignore
components/
attachments-menu.tsx
input-container.tsx
assets/
waveform.png
icon.png
adaptive-icon.png
splash-icon.png
```
- AttachmentsMenu overlay component (attachments-menu.tsx)
- Input bar host component (input-container.tsx)
- Demo App entry (App.tsx)
- Waveform send-button icon asset
- README with install and usage
## Requirements
ChatGPT-Style Attachments Menu targets Expo SDK 56. Versions pinned in the
archive's `package.json`:
- expo ^56.0.8
- 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-glass-effect ~56.0.4
- expo-symbols ~56.0.5
- expo-camera ~56.0.8
- expo-linear-gradient ~56.0.4
- react-native-safe-area-context ^5.8.0
- @legendapp/list ^3.3.0
- typescript ~6.0.3 (dev dependency)
ChatGPT-Style Attachments Menu runs in Expo Go on Expo SDK 56. The modules you
would expect to block it do not: `expo-glass-effect` (the iOS 26 Liquid Glass
material), `expo-camera`, `expo-symbols`, `expo-linear-gradient` and
`react-native-keyboard-controller` are all bundled in Expo Go as of SDK 56, and
`@legendapp/list` is JavaScript only, with no native code at all. A development
build is still what you would ship to production, and it is what you need as
soon as you add any native module Expo Go does not bundle, or a config plugin:
this archive's `app.json` configures `expo-camera` with its own camera
permission string, and a config plugin only takes effect in a build, so in Expo
Go you get Expo Go's permission prompt instead. Two practical caveats either
way: the live camera surface needs a physical device, because `expo-camera`
does not run on simulators, and Liquid Glass only renders its real material on
iOS 26 or newer, falling back to a plain view elsewhere. Liquid Glass and SF
Symbols are iOS APIs, so the finished look is an iOS look. Run it with
`npx expo start` in Expo Go, or `npx expo run:ios` for a development build.
## 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-glass-effect expo-symbols \
expo-camera expo-linear-gradient @legendapp/list
```
## Usage
```tsx
import { useState } from 'react';
import { Pressable, View } from 'react-native';
import { useSharedValue, withSpring } from 'react-native-reanimated';
import { useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller';
import { AttachmentsMenu } from './components/attachments-menu';
export const Composer = () => {
const [menuVisible, setMenuVisible] = useState(false);
const progress = useSharedValue(0);
const extendProgress = useSharedValue(0);
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const openMenu = () => {
setMenuVisible(true);
progress.set(withSpring(1, { duration: 450 }));
};
const closeMenu = () => {
'worklet';
extendProgress.set(withSpring(0, { duration: 450 }));
progress.set(withSpring(0, { duration: 450 }));
};
return (
);
};
```
The shipped `App.tsx` wraps the demo in `SafeAreaProvider`,
`GestureHandlerRootView` and `KeyboardProvider`, then renders
``, which is the full input bar plus the menu already wired
together. `InputContainer` takes no props.
## Customization
`AttachmentsMenu`, the overlay component in ChatGPT-Style Attachments Menu,
takes five props:
- `visible: boolean` (mounts the `OverKeyboardView` overlay)
- `progress: SharedValue` (0 to 1 open animation progress)
- `extendProgress: SharedValue` (0 to 1 extend animation progress)
- `keyboardHeight: SharedValue` (keyboard height, used to anchor the
menu above the input bar)
- `onClose: () => void` (called by the backdrop `Pressable`; in the demo it is
a worklet that drops both drivers to zero)
The rest is edited in source, which is the point of buying the source:
- `MENU_ITEMS` in `components/attachments-menu.tsx` is a four entry `as const`
array of `{ label, symbol, mode }`, where `symbol` is an SF Symbol name and
`mode` is `'photos'` or `'camera'`. Add or relabel rows here.
- `GRID_IMAGES` is 20 placeholder photos from Lorem Picsum. Swap the `uri` for
your own asset URIs or a real media library query.
- The closed, open and extended keyframes are plain numbers inside the four
`useDerivedValue` blocks (closed 46 wide by 46 tall, open 270 by 280,
extended full screen width minus 16). Change the panel size there.
- Spring feel is `withSpring(target, { duration: 450 })` in both
`input-container.tsx` and `attachments-menu.tsx`.
- The photo grid is a `LegendList` with `numColumns={3}`,
`estimatedItemSize={118}` and `recycleItems`.
## When to use it
- An AI assistant or chat app composer that needs an attach button feeling
native to iOS rather than a generic modal.
- A messaging screen where the photo picker should open in place, above the
keyboard, without pushing the conversation off screen.
- Any input bar where you want camera capture and photo selection behind one
control instead of two separate sheets.
- A support or feedback widget that accepts screenshots and photos.
- A comment composer that needs an image attachment step without a full screen
route transition.
- Any product replacing a plain "choose from library or take a photo" action
sheet with a single continuous morph.
## How to get it
- Buy ChatGPT-Style Attachments Menu on its own at
https://motionary.dev/animations/chatgpt-style-attachments-menu, $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 build a ChatGPT style attachment menu in React Native?**
Drive it from two react-native-reanimated shared values and express every
geometry channel as one continuous blend of the closed, open and extended
keyframes with `useDerivedValue`, so both springs can fall to zero together and
collapse in one motion. Motionary's ChatGPT-Style Attachments Menu is that
implementation as editable TypeScript source, at
https://motionary.dev/animations/chatgpt-style-attachments-menu.
**Does ChatGPT-Style Attachments Menu work with Expo Go?**
Yes. Motionary's ChatGPT-Style Attachments Menu runs in Expo Go on Expo SDK 56:
`expo-glass-effect` (the iOS 26 Liquid Glass material), `expo-camera`,
`expo-symbols` and `react-native-keyboard-controller` are all bundled in Expo Go
as of SDK 56, so none of them forces a development build. Use a physical device,
because `expo-camera` does not run on simulators. A development build is still
what you would ship to production, and what you need as soon as you add a native
module Expo Go does not bundle or a config plugin (the `expo-camera` permission
string in this archive's `app.json` only takes effect in a build).
**How do I animate a chat input plus button into a menu without it snapping?**
The snap comes from sequencing two animations and handing off between them.
ChatGPT-Style Attachments Menu avoids it by making the panel's position and
size continuous across all three states, so a close from the extended camera
view is a single spring, never a collapse to the menu items followed by a
second collapse to the plus.
**How do you render a menu above the keyboard in React Native?**
ChatGPT-Style Attachments Menu uses react-native-keyboard-controller:
`OverKeyboardView` hosts the overlay above the keyboard window,
`useReanimatedKeyboardAnimation` feeds live keyboard height into the panel's
animated `bottom`, `left` and `height`, and `KeyboardStickyView` keeps the
input bar itself pinned to the keyboard.
**Which photo grid does it use, and is it recycled?**
The extended photo surface in Motionary's ChatGPT-Style Attachments Menu is a
`@legendapp/list` `LegendList` with `numColumns={3}` and `recycleItems`, and it
is mounted lazily only once the menu extends, so its 20 placeholder images are
not fetched up front.
**Is the Liquid Glass effect iOS only?**
Yes in practice. ChatGPT-Style Attachments Menu renders its panel and buttons
with `expo-glass-effect`, the Liquid Glass material introduced in iOS 26, and
its icons with `expo-symbols` SF Symbols, so the intended target is iOS on Expo
SDK 56. On iOS versions before 26, `GlassView` falls back to a plain view, so
the morph still animates, just without the glass material.
**Do I get the source code, and can I ship it in a commercial app?**
Yes. Buying ChatGPT-Style Attachments Menu on Motionary ($10 USD) is an instant
download of the full TypeScript React Native source, licensed for one developer
to ship in unlimited personal and commercial apps. Do not resell or
redistribute the source itself. Terms: https://motionary.dev/license
## Related drops on Motionary
- Attachment Sheet ($12): https://motionary.dev/animations/attachment-sheet
The Telegram take on the same problem, a Liquid Glass attachment bottom sheet
driven by gesture rather than a morphing plus button.
- Long-Press Menu ($9): https://motionary.dev/animations/long-press-menu
Another chat input overlay built on react-native-keyboard-controller and
glass, opened by long press instead of tap.
- ChatGPT Slider Model Picker ($10):
https://motionary.dev/animations/chatgpt-slider-model-picker
The other half of a ChatGPT style composer, a model selector that lives in
the same input bar and keyboard stack.
- Live Camera Button ($5): https://motionary.dev/animations/live-camera-button
A camera capture control with glass and press states, for apps that want the
camera entry point without the full attachments panel.
- Glass Video Player (Free): https://motionary.dev/animations/glass-video-player
A free way to see how Motionary builds Liquid Glass surfaces and draggable
media controls in React Native.
## 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/chatgpt-style-attachments-menu/llms.txt
Describes: https://motionary.dev/animations/chatgpt-style-attachments-menu
Format: llms.txt (https://llmstxt.org)
Last updated: 2026-09-01
```