跳到正文

davidmokos

expo-glass-tabs

Floating liquid-glass tab bar for Expo Router — minimize-on-scroll, sliding highlight, finger scrubbing with haptics, progressive edge blur

README 已保存到本站,可直接阅读

Documentation snapshot

README 快照

本页保存的是公开项目资料快照,阅读过程不需要连接 GitHub。

expo-glass-tabs

A floating liquid-glass tab bar for Expo Router — the Revolut-style bottom bar.

图片:npm version 图片:npm downloads 图片:TypeScript 图片:license


Why

iOS 26’s native tab bar can minimize on scroll — but it collapses to a single icon. Revolut’s bar keeps all tabs visible and just drops the labels, shrinking the pill in both dimensions. That behavior isn’t reachable from the native UITabBar, so this package rebuilds it on top of Expo Router’s headless tabs — with real native materials.

Features

  • 🪟 Real liquid glass — iOS 26 UIGlassEffect via expo-glass-effect: true squircle corners, rim refraction, content lensing. Solid fallback on older iOS and Android.
  • 📉 Minimize on scroll — scroll down and the pill shrinks in both dimensions while labels collapse; every icon stays visible. Scroll up to expand. Critically-damped springs, no flicker (rubber-band overscroll is filtered out).
  • 🛝 Sliding highlight — the active-tab pill physically travels between tabs on an interruptible, transform-only spring.
  • 👆 Finger scrubbing — drag along the bar: the highlight tracks your finger 1:1, icons light up as you pass them, haptic ticks fire at each boundary, and navigation happens on release — screens never jump mid-drag.
  • 🌫️ Progressive edge blur — content dissolves gradually behind the bar with no hard blur line. The same component works for top bars.
  • 🎞️ Subtle screen transitions — fade + micro-scale between tabs via a TabSlot renderFn.
  • UI-thread everything — all animation runs in Reanimated worklets with native gesture recognizers; a blocked JS thread doesn’t drop a frame.
  • 🧩 Pure TypeScript — no custom native code. Works in Expo Go and any dev build.

Installation

npx expo install expo-glass-tabs expo-blur expo-glass-effect expo-haptics expo-symbols react-native-gesture-handler react-native-reanimated react-native-safe-area-context react-native-screens

Wrap your app root in GestureHandlerRootView (Expo Router does not do this for you):

// app/_layout.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function RootLayout() {
  return {/* ... */};
}

Quick start

import { useRouter } from 'expo-router';
import { Tabs, TabList, TabSlot, TabTrigger } from 'expo-router/ui';
import {
  GlassTabBar,
  GlassTabButton,
  TabBarMinimizeProvider,
  renderFadingTabScreen,
  type GlassTabItem,
} from 'expo-glass-tabs';

const ITEMS: (GlassTabItem & { href: string })[] = [
  { name: 'index', href: '/', label: 'Home', icon: 'house.fill' },
  { name: 'invest', href: '/invest', label: 'Invest', icon: 'chart.line.uptrend.xyaxis' },
  { name: 'payments', href: '/payments', label: 'Payments', icon: 'arrow.left.arrow.right' },
  { name: 'crypto', href: '/crypto', label: 'Crypto', icon: 'bitcoinsign' },
];

export default function AppTabs() {
  const router = useRouter();
  return (
    
      
        
        
           router.navigate(ITEMS[i].href as never)}>
            {ITEMS.map(({ href, ...item }, index) => (
              
                
              
            ))}
          
        
      
    
  );
}

Then attach the scroll hook in every screen that should minimize the bar:

import Animated from 'react-native-reanimated';
import { useMinimizeOnScroll } from 'expo-glass-tabs';

export default function HomeScreen() {
  const onScroll = useMinimizeOnScroll();
  return (
    
      {/* content */}
    
  );
}

Custom icons

Use renderIcon when an SF Symbol won’t do — a brand logo, for example. It’s called once per tint layer, so the active/inactive crossfade stays on the UI thread:

import { Image } from 'expo-image';

const home: GlassTabItem = {
  name: 'index',
  label: 'Home',
  renderIcon: ({ tint }) => (
    
  ),
};

Theming

Progressive blur for your own edges

ProgressiveBlur is exported on its own — drop it behind a transparent header:

import { ProgressiveBlur } from 'expo-glass-tabs';

;

API

ExportKindPurpose
GlassTabBarcomponentThe floating pill — use via TabList asChild
GlassTabButtoncomponentOne trigger — use via TabTrigger asChild
TabBarMinimizeProvidercomponentWrap the Tabs tree once
useMinimizeOnScroll()hookScroll handler for Animated.ScrollView
useTabBarMinimized()hookRaw 0..1 minimize progress, for custom UI
renderFadingTabScreenfunctionTabSlot renderFn with fade + micro-scale
ProgressiveBlurcomponentGradient blur anchored to a screen edge
MINIMIZE_SPRINGconstantThe spring config, if you want to match it

How it works

The bar’s structure is declared in JS (Expo Router headless tabs), but everything you see and feel at runtime is native:

  • MaterialsUIGlassEffect, UIVisualEffectView, SF Symbols, UIFeedbackGenerator.
  • Motion — Reanimated worklets on the UI thread; the sliding highlight and scrub are transform-only (GPU-composited, no layout work per frame).
  • Gestures — native recognizers via react-native-gesture-handler; a Pan (scrub) races a Tap, so taps stay forgiving while drags feel attached to the finger.

The corner geometry is rendered by the glass view itself (its native corner configuration), not an RN clipping mask — that’s what preserves the true squircle and the rim lighting through the whole minimize animation.

License

MIT © David Mokos

Official distribution

获取与安装

以下地址来自本站保存的 README,并指向对应生态的官方软件包页面。本站不托管安装包或二进制文件。

安装前请在官方包页核对包名、维护者、版本和签名;具体命令以该项目 README 与官方文档为准。

使用前核验

本站保存公开资料用于阅读,不代表安全审计或功能背书。安装前请核对许可证、依赖来源和发布签名,不要直接运行来源不明的二进制文件或高权限脚本。