npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rn-liquid-glass-bottom-tabs

v0.3.5

Published

Native iOS 26 liquid-glass bottom tabs for React Native — draggable droplet pill, ripple animation, React Navigation integration, 3-tier fallback

Readme

npm npm downloads CI license

Native iOS 26 liquid-glass bottom tabs for React Native.
Draggable droplet pill · Ripple animation · React Navigation drop-in · 3-tier fallback.


Demo

| iOS 26 — native UIGlassEffect | iOS 13–25 — UITabBarController fallback | | --- | --- | | ▶ Watch on Google Drive | ▶ Watch on Kapwing |

Features

  • 💧 Native iOS 26 liquid glass — wraps Apple's UIGlassEffect directly via Fabric / New Architecture
  • 👆 Drag-to-select — swipe across tabs; the droplet pill follows your finger and snaps to the nearest tab on release
  • 🌊 Ripple animation — tabs flex and scale as the droplet passes over them, like water deforming what's underneath
  • 🪄 Magnify on grab — pill grows symmetrically the moment you start dragging
  • ⚙️ Configurable animation — spring or timing, with full config passthrough to Animated
  • 🎨 Fully customizable — tint color, color scheme, pill border radius, snap threshold, arbitrary icon/label ReactNode
  • 📱 3-tier fallback — iOS 26 UIGlassEffect → iOS 13–25 UIBlurEffect / UITabBarController → plain JS row on Android
  • 🧭 React Navigation integrationcreateLiquidGlassTabNavigator is a drop-in for createBottomTabNavigator

Requirements

| Requirement | Minimum | | --- | --- | | React Native | ≥ 0.80 with New Architecture (Fabric) enabled | | Xcode | 26 | | iOS at runtime | 13+ (liquid glass requires iOS 26+) | | React | ≥ 18 |

New Architecture: This library uses Fabric native components and TurboModules. If you are still on the old architecture, follow the migration guide before installing.

Expo: Not compatible with Expo Go. Use a development build or Expo Prebuild (bare workflow). See Expo below.

Android: Gets a plain JavaScript tab row — the same API works everywhere; only the glass effect is iOS-only.

Which component should I use?

| Want… | Use | | --- | --- | | The exact iOS 26 liquid glass tab bar (system look, SF Symbols, scroll-to-minimize, badges) | LiquidGlassTabView | | Custom icons / labels (any ReactNode), draggable droplet, ripple, custom animation & styling | LiquidGlassTabBar |

  • LiquidGlassTabView wraps SwiftUI's native TabView on iOS 26 — you get Apple's tab bar exactly as it ships. Icons are SF Symbol names (strings). On iOS < 26 it falls back to UITabBarController. On Android it renders a plain JS row.
  • LiquidGlassTabBar is a fully-JS tab bar built on LiquidGlassView / UIGlassEffect. Icons and labels are arbitrary ReactNode (use any icon library, emoji, images). You get the draggable droplet, ripple animation, and full styling/animation control.

If you're integrating with React Navigation and want the standard iOS tab bar UX, use createLiquidGlassTabNavigator (built on LiquidGlassTabView). If you want a bespoke, branded tab bar, use LiquidGlassTabBar as the navigator's tabBar prop.

Installation

npm install rn-liquid-glass-bottom-tabs
# or
yarn add rn-liquid-glass-bottom-tabs

No additional pod install step is needed — CocoaPods picks up the podspec automatically on the next build.

Quick start — LiquidGlassTabBar (custom, draggable)

import { useState } from 'react';
import { Text } from 'react-native';
import { LiquidGlassTabBar } from 'rn-liquid-glass-bottom-tabs';

export function BottomTabs() {
  const [activeIndex, setActiveIndex] = useState(0);

  const tabs = [
    { key: 'home',    icon: <Text>🏠</Text>, label: <Text>Home</Text> },
    { key: 'search',  icon: <Text>🔍</Text>, label: <Text>Search</Text> },
    { key: 'profile', icon: <Text>👤</Text>, label: <Text>Profile</Text> },
  ];

  return (
    <LiquidGlassTabBar
      tabs={tabs}
      activeIndex={activeIndex}
      onTabPress={setActiveIndex}
      draggable
      animation={{
        type: 'spring',
        config: { damping: 16, stiffness: 220, mass: 0.8 },
      }}
      pillBorderRadius={28}
      style={{ position: 'absolute', bottom: 50, left: 20, right: 20 }}
    />
  );
}

Icons and labels are arbitrary ReactNode — bring your own icon library (Ionicons, Lucide, SF Symbols, emoji, images, anything).

Quick start — LiquidGlassTabView (native, system look)

Use this when you want the exact iOS 26 liquid glass tab bar. Icons are SF Symbol names.

import { useState } from 'react';
import { View, Text } from 'react-native';
import { LiquidGlassTabView } from 'rn-liquid-glass-bottom-tabs';

export function NativeTabs() {
  const [selectedKey, setSelectedKey] = useState('home');

  return (
    <LiquidGlassTabView
      tabs={[
        { key: 'home',    title: 'Home',    sfSymbol: 'house.fill' },
        { key: 'search',  title: 'Search',  sfSymbol: 'magnifyingglass' },
        { key: 'profile', title: 'Profile', sfSymbol: 'person.fill', badge: '3' },
      ]}
      selectedKey={selectedKey}
      onTabChange={setSelectedKey}
      minimizeBehavior="onScrollDown"
      tintColor="#007AFF"
    >
      <View collapsable={false}><Text>Home screen</Text></View>
      <View collapsable={false}><Text>Search screen</Text></View>
      <View collapsable={false}><Text>Profile screen</Text></View>
    </LiquidGlassTabView>
  );
}

[!IMPORTANT] Wrap each tab screen's root view in collapsable={false}.

LiquidGlassTabView hosts your React Native screens inside native iOS UIViewControllers. React Native's view-flattening optimization can collapse a screen's outermost <View> — but that leaves the host view controller with no view to mount, so the screen renders blank or with broken layout.

Fix: add collapsable={false} to the outermost view of every screen:

function HomeScreen() {
  return (
    <View collapsable={false} style={{ flex: 1 }}>
      {/* screen content */}
    </View>
  );
}

What you get per iOS version:

| iOS version | What renders | | --- | --- | | iOS 26+ | SwiftUI TabView with the genuine liquid glass tab bar | | iOS 13–25 | UIKit UITabBarController with the translucent system tab bar | | Android / other | Plain JS tab row (LiquidGlassTabViewFallback) |

minimizeBehavior ('automatic' | 'never' | 'onScrollDown' | 'onScrollUp') only applies on iOS 26+; it's ignored on the fallback.

React Navigation — native bar (createLiquidGlassTabNavigator)

Drop-in replacement for createBottomTabNavigator that renders LiquidGlassTabView under the hood.

import { NavigationContainer } from '@react-navigation/native';
import { createLiquidGlassTabNavigator } from 'rn-liquid-glass-bottom-tabs';

const Tab = createLiquidGlassTabNavigator();

export function Tabs() {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={{
          tabBarTintColor: '#FF3B30',
          tabBarMinimizeBehavior: 'onScrollDown', // iOS 26 only
        }}
      >
        <Tab.Screen
          name="Home"
          component={HomeScreen}
          options={{ title: 'Home', sfSymbol: 'house.fill' }}
        />
        <Tab.Screen
          name="Search"
          component={SearchScreen}
          options={{ title: 'Search', sfSymbol: 'magnifyingglass' }}
        />
        <Tab.Screen
          name="Profile"
          component={ProfileScreen}
          options={{ title: 'Profile', sfSymbol: 'person.fill', badge: '3' }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

[!IMPORTANT] Each screen component must have collapsable={false} on its outermost view — see the note above.

[!NOTE] createLiquidGlassTabNavigator requires @react-navigation/native as a peer dependency. It's marked optional — if you only use LiquidGlassTabBar you don't need React Navigation installed.

Per-screen options

| Option | Type | Description | | --- | --- | --- | | title | string | Tab label. Falls back to the route name. | | sfSymbol | string | SF Symbol name for the tab icon (e.g. 'house.fill'). | | badge | string | Badge text shown on the tab (e.g. '3', 'NEW'). | | tabBarTintColor | ColorValue | Selected-tab tint. Bar-wide — set in screenOptions. | | tabBarMinimizeBehavior | 'automatic' \| 'never' \| 'onScrollDown' \| 'onScrollUp' | Scroll-driven minimize. iOS 26 only. |

What tabBarTintColor colors

  • iOS 26: selected tab's icon + label tint.
  • iOS 13–25: UITabBar.tintColor — selected tab's icon/label color.
  • Android / JS fallback: active tab label color.

The glass background itself isn't tintable — put a colored view behind your screens if you want the bar to look tinted.

React Navigation — custom bar (LiquidGlassTabBar)

Use @react-navigation/bottom-tabs and pass LiquidGlassTabBar as the tabBar prop to get the draggable droplet bar instead of the native one.

import { Text } from 'react-native';
import {
  createBottomTabNavigator,
  type BottomTabBarProps,
} from '@react-navigation/bottom-tabs';
import { LiquidGlassTabBar } from 'rn-liquid-glass-bottom-tabs';

const Tab = createBottomTabNavigator();

function GlassTabBar({ state, descriptors, navigation }: BottomTabBarProps) {
  const tabs = state.routes.map((route) => {
    const { options } = descriptors[route.key];
    const label =
      typeof options.tabBarLabel === 'string'
        ? options.tabBarLabel
        : (options.title ?? route.name);

    return {
      key: route.key,
      icon: options.tabBarIcon?.({ focused: false, color: 'white', size: 22 }),
      label: <Text style={{ color: 'white', fontSize: 13 }}>{label}</Text>,
      accessibilityLabel: options.tabBarAccessibilityLabel,
    };
  });

  return (
    <LiquidGlassTabBar
      tabs={tabs}
      activeIndex={state.index}
      onTabPress={(index) => {
        const route = state.routes[index];
        const event = navigation.emit({
          type: 'tabPress',
          target: route.key,
          canPreventDefault: true,
        });
        if (!event.defaultPrevented) {
          navigation.navigate(route.name, route.params);
        }
      }}
      draggable
      style={{ position: 'absolute', bottom: 30, left: 20, right: 20 }}
    />
  );
}

export function Tabs() {
  return (
    <Tab.Navigator
      screenOptions={{ headerShown: false }}
      tabBar={(props) => <GlassTabBar {...props} />}
    >
      <Tab.Screen
        name="Home"
        component={HomeScreen}
        options={{
          tabBarIcon: ({ color }) => <HomeIcon color={color} />,
          tabBarLabel: 'Home',
        }}
      />
      <Tab.Screen name="Search" component={SearchScreen} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
}

Notes:

  • state.routestabs, state.indexactiveIndex; tab presses go through React Navigation's navigate so tabPress events work (scroll-to-top pattern, etc.)
  • For screens not to hide behind the floating bar, add paddingBottom: 100 (or use useSafeAreaInsets + bar height)
  • tabBarStyle: { display: 'none' } is not needed — React Navigation only renders the bar you pass via tabBar

LiquidGlassTabBar props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | tabs | LiquidGlassTabBarItem[] | — | Array of { key, icon?, label?, accessibilityLabel? } | | activeIndex | number | — | Index of the currently active tab | | onTabPress | (index, tab) => void | — | Fires on tap and on drag-snap | | draggable | boolean | false | Enable drag-to-select with droplet ripple + magnify | | dragSnapThreshold | number (0–1) | 0.5 | Fraction of a tab's width the drag must cross before snapping | | animation | LiquidGlassTabBarAnimation | spring (iOS-like) | How the pill animates between tabs | | pillBorderRadius | number | 999 | Active-pill border radius. Default is fully rounded | | effect | 'clear' \| 'regular' \| 'none' | 'regular' | Glass effect of the active pill | | tintColor | ColorValue | undefined | Tint for the bar background | | activeTintColor | ColorValue | undefined | Tint for the active pill (falls back to tintColor) | | colorScheme | 'light' \| 'dark' \| 'system' | 'system' | Override the glass color scheme | | spacing | number | 20 | Distance for the LiquidGlassContainerView merge effect | | style | StyleProp<ViewStyle> | — | Outer wrapper style — use to position absolutely | | tabStyle | StyleProp<ViewStyle> | — | Style for each tab's Pressable | | activeTabStyle | StyleProp<ViewStyle> | — | Extra style for the active pill |

Animation prop reference

type LiquidGlassTabBarAnimation =
  | { type: 'spring'; config?: { damping?: number; stiffness?: number; mass?: number; overshootClamping?: boolean } }
  | { type: 'timing'; config?: { duration?: number; easing?: (v: number) => number } }
  | { type: 'none' }; // sets value instantly, no animation

Lower-level primitives

import {
  LiquidGlassView,
  LiquidGlassContainerView,
  isLiquidGlassSupported,
  isLegacyGlassSupported,
} from 'rn-liquid-glass-bottom-tabs';

if (!isLiquidGlassSupported) {
  // Render a non-glass fallback
}

<LiquidGlassContainerView spacing={20}>
  <LiquidGlassView interactive effect="clear" style={{ width: 100, height: 100, borderRadius: 50 }} />
  <LiquidGlassView interactive effect="clear" style={{ width: 100, height: 100, borderRadius: 50 }} />
</LiquidGlassContainerView>

For text inside glass that auto-adapts to the surface behind it, use PlatformColor:

import { PlatformColor, Text } from 'react-native';
import { LiquidGlassView } from 'rn-liquid-glass-bottom-tabs';

<LiquidGlassView style={{ padding: 20, borderRadius: 20 }}>
  <Text style={{ color: PlatformColor('labelColor') }}>Hello World</Text>
</LiquidGlassView>

[!NOTE] Fallback behavior (iOS only — Android always renders a plain View):

  • iOS 26+ → real liquid glass via UIGlassEffect
  • iOS 13–25 → UIBlurEffect with system materials (systemUltraThinMaterial for regular, systemThinMaterial for clear)
  • isLiquidGlassSupportedtrue only on iOS 26 with UIGlassEffect
  • isLegacyGlassSupportedtrue on any iOS (legacy or new); false on Android

Expo

This library requires native code and does not work in Expo Go.

Development builds (recommended):

npx expo install rn-liquid-glass-bottom-tabs
npx expo prebuild        # regenerates native ios/ folder
npx expo run:ios         # builds and runs on simulator/device

Bare workflow: follows the standard installation steps above.

Expo managed workflow: not supported. The library requires New Architecture and Xcode 26, which Expo SDK's managed runtime doesn't yet expose.

Troubleshooting

Screen renders blank / stretched inside LiquidGlassTabView Add collapsable={false} to the outermost <View> of each tab screen. See the note above.

Glass effect not showing on iOS

  • Confirm you're running on a device or simulator with iOS 26+.
  • Check isLiquidGlassSupported at runtime — it returns false if the device runs iOS < 26 or if UIDesignRequiresCompatibility is set to YES in your Info.plist.
  • On iOS 13–25 you'll get the UIBlurEffect fallback instead (also a glass look, just not the iOS 26 morph).

Build error: "No such module 'React'" Run pod install inside example/ios/ (or your app's ios/ folder) after installing.

"NativeLiquidGlassModule not found" New Architecture must be enabled. Add RCT_NEW_ARCH_ENABLED=1 to your .xcode.env.local (or set newArchEnabled=true in android/gradle.properties) and rebuild.

Module works on iOS but tab shows blank on first render Add collapsable={false} to the screen root view. This is the most common issue.

Contributing

See CONTRIBUTING.md for the development workflow, commit conventions, and pull request process.

Quick start:

git clone https://github.com/Kashifstar151/rn-liquid-glass-bottom-tabs.git
cd rn-liquid-glass-bottom-tabs
yarn                    # installs all workspace deps
yarn example ios        # build and run the example app

Credits

Inspired by and initially built on top of @callstack/liquid-glass by Callstack, which pioneered the UIGlassEffect Fabric component approach for React Native. This package adds a higher-level LiquidGlassTabBar with drag-to-select, ripple, configurable animations, and a createLiquidGlassTabNavigator React Navigation integration.

Changelog

See CHANGELOG.md for release history.

License

MIT © Kashif Khan


If this saved you time, a ⭐ on GitHub is hugely appreciated — it helps others discover the project.

Support on Gumroad · Report an issue · Contributing