Hide-on-scroll
Pre-alphaThe registry and the CLI are not published yet.Roadmap
Scroll

Hide-on-scroll

Bars slide out while the user scrolls down and come back as soon as they scroll up.

Draft

Specified, not implemented yet. The API is described in useHideOnScroll and may change.

While the user scrolls down a list, the top and bottom bars slide out of the screen and the content gets the room. The moment they scroll up, even in the middle of the list, the bars come back.

GH
Grace HopperFound the bug. It was a moth.
AT
Alan TuringCan a machine think? Lunch first.
KJ
Katherine JohnsonTrajectory numbers attached.
LT
Linus TorvaldsJust a hobby, won’t be big.
MH
Margaret HamiltonPriority alarms are fine.
AL
Ada LovelaceNotes on the engine, part G.
DR
Dennis Ritchiehello, world
BL
Barbara LiskovAbout that substitution…
ED
Edsger DijkstraGoto considered harmful.
FA
Frances AllenThe compiler is faster now.
KT
Ken ThompsonTrust nothing you didn’t compile.
RP
Radia PerlmanThe spanning tree converged.
JM
John McCarthy(lambda (x) (lunch x))
HL
Hedy LamarrFrequency hopping, patent filed.
TB
Tim Berners-LeePut it on a web page.
DK
Donald KnuthVolume 4 is almost ready.
SG
Shafi GoldwasserProof attached, zero knowledge.
Gv
Guido van RossumThere should be one way to do it.
Feed
FeedExploreActivityProfile
hidden 0 · progress 0.00 · scroll the list

Scroll the list, with the wheel, a trackpad or by dragging it. Stop half-way and the bars snap to the nearest end.

The same behavior, frame by frame:

at top
hidden 0

Bars always visible near the top.

scrolling down
hidden 24

Bars leave with the finger, pt for pt.

hidden
hidden 40

Clamped at the bar height. Content gets the room.

scrolling up
hidden 24

Any scroll up brings them back, mid-list.

released
hidden 0

Half-way on release: snaps to the nearest end.

When to use it

  • Long feeds and lists read from top to bottom: news, social feed, search results, a long article.
  • Bars that are useful but not needed while reading: AppBar, BottomTabBar, a floating action button.

Don’t hide a bar that holds the main action of the screen (a checkout button, a composer), short lists that barely scroll, or a screen where the user switches tabs often.

How it works

The bars don’t follow the scroll position, they follow the scroll delta. hidden is how many points of the bar are out of view, between 0 and the bar height:

hidden = clamp(hidden + (offset - previousOffset), 0, height);
offset  0 ──────── 24 ──────── 72 ──────── 56 ──────── 52
delta          +24         +48         -16          -4
hidden  0 ──────── 24 ──────── 40 ──────── 24 ──────── 20 ──► release: snaps to 0
                          (clamped)

Four rules make it feel right:

RuleWhy
Clamp between 0 and heightScrolling up 16pt after a long scroll down shows 16pt of bar, not nothing.
Near the top, hidden never exceeds offsetThe bars are always complete when the list is at its start.
Ignore bounces: offsets below 0 or past the end of the contentOn iOS the bounce would otherwise move the bars back and forth.
Snap on release: at the end of the drag or the momentum, animate to 0 or height, whichever is nearestA bar is never left half-hidden.

Both bars read the same hidden: the top bar moves by -hidden, the bottom bar by +hidden.

Implementation

Axiom implements it with one hook, useHideOnScroll, that returns the value and the scroll props, and leaves the bars untouched.

LibraryUsed for
react-native-reanimatedhidden as a shared value computed in useAnimatedScrollHandler (onScroll, onEndDrag, onMomentumEnd), so the bars move on the UI thread with no React render while scrolling. withTiming for the snap.
React Native layoutThe bars are positioned over the list, and the list gets top and bottom padding equal to the bar heights, so nothing is covered at rest.

react-native-gesture-handler isn’t needed: the scroll events are enough, on iOS and Android.

In a screen

Conceptual
const bars = useHideOnScroll({ height: 56 });

<Animated.FlatList
	data={posts}
	renderItem={renderPost}
	{...bars.scrollProps}
/>;
// top bar: translateY -bars.hidden.value, bottom bar: +bars.hidden.value

Installation, options and return value are documented in useHideOnScroll. Components that support it take the value directly, like hideOnScroll on SearchBar in an AppBar.

Accessibility

  • When a screen reader is on (AccessibilityInfo.isScreenReaderEnabled), keep the bars shown: moving focus through a list shouldn’t take the navigation away.
  • Call show() when focus moves into a bar or a keyboard opens.
  • With Reduce Motion on, skip the snap animation and jump to the nearest end.