# Scroll Hiding Menu: React Native scroll-aware floating tab bar > Scroll Hiding Menu is a React Native and Expo component that collapses a > floating pill tab bar into a three dot indicator when the user scrolls down a > list, then springs it back open the moment they scroll up. It is a Motionary > drop: one Reanimated `progress` shared value on the UI thread drives the pill > width, the icons and the dots, shipped as editable TypeScript source. Product page: https://motionary.dev/animations/scroll-hiding-menu Price: $3 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, react native Rarity: rare Sold by: Motionary (https://motionary.dev) ## What it is Scroll Hiding Menu is a floating bottom navigation bar for React Native and Expo apps that reacts to scroll direction. Scroll down and the pill fades out as it shrinks to a 50pt circle, leaving a row of three small dots sitting where it was; scroll up and it fades back in and grows into a 160pt wide light pill holding three SF Symbol icons, while the dots slide apart and fade away. The navigation gets out of the way while the user is reading and returns as soon as they reverse direction. The demo screen in the Motionary archive is a music list (16 song titles with artists) inside an `Animated.ScrollView`, but the component is written against any scrollable surface: a `FlatList`, a `ScrollView`, a feed. ## What it does - Detects scroll direction on the UI thread and maps it to a single `progress` shared value between 0 (collapsed, dots visible) and 1 (pill open). - Animates the pill width from `MENU_COLLAPSED_WIDTH` (50) to `MENU_EXPANDED_WIDTH` (160) with `interpolate`, plus a matching opacity fade. - Scales the three icons from 0.4 to 1 and slides the left and right icons in by `MENU_ITEM_TRAVEL` (32pt) so they converge as the pill closes. - Cross-fades a three dot indicator against the pill: the outer dots translate out by `DOT_TRAVEL` (34pt) and the whole row fades to 0 as `progress` hits 1. - Uses `withSpring` for every transition, so a direction change mid animation is picked up smoothly instead of restarting a timing curve. - Ignores scroll events once the list has reached its bottom (`layoutMeasurement.height + contentOffset.y >= contentSize.height`), so bounce-back at the end of a list does not read as an "up" scroll and pop the menu open. - Ignores non-positive offsets (`value <= 0`), so overscroll at the top of the list does not fight the animation. - Renders icons with `SymbolView` from `expo-symbols` (`music.note.list`, `house`, `slider.horizontal.2.square`), so the icons are native SF Symbols on iOS. SF Symbols are an iOS API and the shipped `Container.tsx` passes no `fallback`, so on Android swap `SymbolView` for an icon set such as `@expo/vector-icons`. - Contains no native code of its own: it is plain TypeScript, Reanimated and Expo modules in a managed Expo project. ## How it works Scroll Hiding Menu keeps the entire reveal on the UI thread with `react-native-reanimated`, and the whole technique lives in one file, `components/Container.tsx`. Two shared values are created with `useSharedValue`: `offsetY` (the latest scroll position) and `progress` (the 0 to 1 openness of the menu). An `useAnimatedScrollHandler` is passed to `Animated.ScrollView`'s `onScroll` prop and writes `event.contentOffset.y` into `offsetY` on every scroll event, except when `event.layoutMeasurement.height + event.contentOffset.y >= event.contentSize.height`, which is the guard that stops end-of-list bounce from being read as a scroll up. Direction is then derived by `useAnimatedReaction`, which receives both the current and the previous value of `offsetY`: if the value grew the user is scrolling down and `progress.value = withSpring(0)`, otherwise `progress.value = withSpring(1)`; the reaction bails out when `previous` is `null` (first run) or when the offset is at or above the top. Every visual property is then a pure function of that one value through `useAnimatedStyle` plus `interpolate`: `menuStyle` interpolates `width` and `opacity`, `menuItemLeftStyle`, `menuItemCenterStyle` and `menuItemRightStyle` interpolate `scale` and `translateX` for the icons, and `dotsStyle`, `leftDotStyle` and `rightDotStyle` interpolate the indicator's opacity and spread. Because the handler, the reaction and all seven animated styles are Reanimated worklets, nothing crosses to the JavaScript thread per frame and the pill, the icons and the dots can never drift out of sync: they read the same spring. The pill and the dot row are absolutely positioned siblings above the scroll view (`zIndex` 10 and 100), which is why the swap between them reads as one shape morphing rather than two components toggling. `App.tsx` only wraps the scene in `GestureHandlerRootView` from `react-native-gesture-handler`. ## What you get ``` components/ Container.tsx App.tsx index.ts app.json package.json package-lock.json tsconfig.json README.md assets/ ``` - TypeScript source (`components/Container.tsx`) - Demo (runnable Expo app: `App.tsx`, `index.ts`, `app.json`, `assets/`) - README with install and usage ## Requirements Versions pinned in the Scroll Hiding Menu archive's `package.json`: - `expo` ^56.0.3 - `react-native` 0.85.3 - `react` 19.2.3 - `react-native-reanimated` 4.3.1 - `react-native-worklets` 0.8.3 (required by Reanimated 4) - `react-native-gesture-handler` ~2.31.1 - `expo-symbols` ~56.0.5 - `typescript` ~6.0.3 (dev dependency) Expo Go or development build: Scroll Hiding Menu runs in Expo Go on Expo SDK 56, and so does the demo project as Motionary ships it. Everything the drop imports is bundled in the Expo Go client: `components/Container.tsx` uses only `react-native-reanimated` (with its `react-native-worklets` runtime) and `expo-symbols`, and `App.tsx` adds `react-native-gesture-handler`. There is no custom native code. The archive is a whole Expo starter project, so its `package.json` also pins scaffold dependencies the drop never imports (`@expo/ui`, `@expo/vector-icons`, `expo-blur`, `expo-video`, `expo-sensors`, `expo-glass-effect`, `react-native-skia-gesture`, `react-native-svg`, `pressto`); leave them out of your own project. `expo-glass-effect` is itself bundled in Expo Go as of SDK 56, so it does not force a build either. A development build (`npx expo run:ios` or `npx expo run:android`, the commands the archive's README uses) is still what you would ship to production. (The archive's README text mentions Expo SDK 54, while the shipped `package.json` pins the Expo SDK 56 line; trust the `package.json`.) ## Install ```bash npx expo install react-native-reanimated react-native-worklets react-native-gesture-handler expo-symbols ``` ## Usage ```tsx // App.tsx, as shipped in the Motionary Scroll Hiding Menu archive import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { Container } from './components/Container'; // Container renders the scrollable list, the animated pill menu and the // three dot indicator together. It takes no props: the demo list, the SF // Symbol names and the tuning constants all live in components/Container.tsx, // so you customise it by editing that file (or by lifting the progress / // offsetY / scrollHandler block into your own screen). export default function App() { return ( ); } ``` ## Customization `Container` from Scroll Hiding Menu takes no props; it is tuned through module-level constants and styles in `components/Container.tsx`: - `MENU_EXPANDED_WIDTH` (number, 160): width of the open pill in points. - `MENU_COLLAPSED_WIDTH` (number, 50): collapsed width; it matches the menu's `height: 50` and `borderRadius: 25`, so the closed state is a perfect circle. - `MENU_ITEM_TRAVEL` (number, 32): how far the left and right icons slide in while the pill closes. - `DOT_TRAVEL` (number, 34): how far the outer two dots spread apart as the pill opens. - The `withSpring(0)` and `withSpring(1)` calls inside `useAnimatedReaction` accept a standard Reanimated spring config if you want a different damping, stiffness or mass. - `SymbolView` `name` values (`music.note.list`, `house`, `slider.horizontal.2.square`) and `tintColor` (`#9B9A9C` for the inactive icons, `black` for the active one) set the three tabs. - Styles: `menu` (`bottom: 30`, `backgroundColor: '#EBEBED'`), `dot` (8x8 white, `borderRadius: 8`), `dotDim` (`opacity: 0.3`), and `dotsContainer` (`bottom: 50`, `gap: 8`). - `SONG_TITLES` and `ARTISTS` are demo data arrays; replace them or swap `Animated.ScrollView` for your own `Animated.FlatList`, keeping the `onScroll={scrollHandler}` wiring. ## When to use it - A music, podcast or media list where the bottom tab bar should yield to artwork and titles while the user scrolls. - A social or content feed that wants maximum reading area but instant access to navigation on the first upward flick. - A long settings or profile screen where a persistent floating action pill would otherwise cover the last row. - A shopping or catalog grid where the filter and cart pill should collapse while browsing and return when the user scrolls back toward the top. - Any screen where you want an attention-aware bottom navigation instead of a tab bar that is always on screen or always hidden. ## How to get it - Buy this drop on its own at https://motionary.dev/animations/scroll-hiding-menu, $3, 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 hide a tab bar on scroll in React Native?** The approach used by Motionary's Scroll Hiding Menu is to write the scroll offset into a shared value with `useAnimatedScrollHandler`, compare the current offset to the previous one inside `useAnimatedReaction` to get the direction, and spring a single `progress` value between 0 and 1 that every animated style interpolates from. That keeps the whole hide and reveal on the UI thread with `react-native-reanimated`, with no per frame JavaScript work. **Does Scroll Hiding Menu work with a FlatList?** Yes. Scroll Hiding Menu's demo in the Motionary archive uses an `Animated.ScrollView`, but the logic only needs an `onScroll` handler from Reanimated, so passing the same `scrollHandler` to an `Animated.FlatList` (or any Reanimated-wrapped scrollable) drives the pill identically. **Does Scroll Hiding Menu work in Expo Go, or do I need a development build?** It runs in Expo Go. Motionary's Scroll Hiding Menu imports only `react-native-reanimated` (with its `react-native-worklets` runtime), `react-native-gesture-handler` and `expo-symbols`, all of which ship inside the Expo Go client on Expo SDK 56, and it adds no custom native code. The archive's `package.json` also pins unused Expo starter dependencies such as `expo-glass-effect` and `react-native-skia-gesture`; nothing in the drop imports them, so you can leave them out of your own project. **Does the floating tab bar look right on Android?** Scroll Hiding Menu's animation is pure Reanimated and is identical on both platforms, but its icons are rendered with `SymbolView` from `expo-symbols`, and SF Symbols are an iOS API, so the icons render on iOS only and the shipped `Container.tsx` passes no `fallback` for other platforms. Swapping `SymbolView` for `@expo/vector-icons` (already pinned in the archive's `package.json`) or your own SVGs in `components/Container.tsx` gives Android parity. **Why does my hide-on-scroll menu pop open at the bottom of the list?** Because iOS bounce at the end of a list reverses the offset and reads as a scroll up. Scroll Hiding Menu solves this inside its `useAnimatedScrollHandler` by returning early when `layoutMeasurement.height + contentOffset.y >= contentSize.height`, so the offset is never written while the list is bouncing at its end. **How much does Scroll Hiding Menu cost and what do I get?** Scroll Hiding Menu is $3 USD on Motionary (https://motionary.dev/animations/scroll-hiding-menu, price as of 2026-09-01), and checkout is an instant download of the full TypeScript source (`components/Container.tsx` plus a runnable Expo demo and a README). It is also covered by the Motionary lifetime pass ($79 USD) at https://motionary.dev/pricing. ## Related drops on Motionary - Mail Tab Bar (Free): https://motionary.dev/animations/mail-tab-bar another Reanimated bottom bar, this one a segmented Apple Mail style tab bar rather than a scroll-aware one. - Infinite Loophole (Free): https://motionary.dev/animations/infinite-loophole a bottom-tabs navigation experiment with a parallax card stack, useful next to a collapsing tab bar. - Gooey Profile (Free): https://motionary.dev/animations/gooey-profile the same scroll-driven idea applied to a header: a Skia metaball profile header that reacts to list scroll. - Mini-Player Sheet (Free): https://motionary.dev/animations/mini-player-sheet a music mini-player that expands into a now-playing sheet, pairs naturally with Scroll Hiding Menu on a music list screen. - Stacked Cards ($4): https://motionary.dev/animations/stacked-cards another scroll-driven interpolation drop, snapping stacked cards inside a scrollable list. ## 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/scroll-hiding-menu/llms.txt Describes: https://motionary.dev/animations/scroll-hiding-menu Format: llms.txt (https://llmstxt.org) Last updated: 2026-09-01 ```