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.3

Published

Liquid Glass bottom tab bar for React Native (iOS 26+)

Readme

npm version npm downloads mit licence

A drop-in iOS 26 liquid-glass bottom tab bar for React Native — with a draggable droplet pill, ripple animation, and configurable spring/timing animations.

Demo

Which component should I use?

This package ships two tab bars. Pick the one that matches what you want:

| 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 is a thin wrapper around SwiftUI's native TabView on iOS 26 — you get Apple's tab bar exactly as it ships in iOS 26 apps. Icons are SF Symbol names (strings). On iOS < 26 it falls back to a UITabBarController (translucent system tab bar — the "previous liquid glass" look). On Android it falls back to a plain JS tab row.
  • LiquidGlassTabBar is a fully-JS tab bar built on top of 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 — but it's your bar, not the system one.

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.

Highlights

  • 💧 LiquidGlassTabBar — bottom tab bar built on iOS 26's native UIGlassEffect
  • 👆 Drag-to-select — swipe across tabs; the droplet pill follows your finger and snaps to the nearest tab on release
  • 🌊 Droplet ripple — tabs flex/scale as the droplet passes over them, like water deforming what's underneath
  • 🪄 Magnify on grab — pill grows symmetrically (top/bottom/left/right) the moment you start dragging
  • ⚙️ Configurable animation — spring or timing, with full config passthrough
  • 🎨 Customizable — tint color, active tint color, color scheme, pill border radius, snap threshold
  • 📱 3-tier fallback — iOS 26 liquid glass → iOS 13–25 UIBlurEffect materials / UITabBarController → plain JS row on Android

Also re-exports the underlying primitives so you can build other glass UIs:

  • LiquidGlassTabView — native SwiftUI tab view (iOS 26) with UITabBarController fallback
  • LiquidGlassView — single glass surface
  • LiquidGlassContainerView — merges sibling glass elements (the morph effect)
  • createLiquidGlassTabNavigator — React Navigation integration for the native tab view
  • isLiquidGlassSupported — true when iOS 26 liquid glass APIs are available
  • isLegacyGlassSupported — true when the UIKit fallback (UIBlurEffect / UITabBarController) is available (any iOS)

Installation

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

[!WARNING] Compile your app with Xcode ≥ 26 and React Native ≥ 0.80. New Architecture (Fabric) is required.

[!WARNING] Not supported in Expo Go — use a development build.

Quick start

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, anything).

Quick start — LiquidGlassTabView (native, system look)

Use this if 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><Text>Home screen</Text></View>
      <View><Text>Search screen</Text></View>
      <View><Text>Profile screen</Text></View>
    </LiquidGlassTabView>
  );
}

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 to the iOS 26 SwiftUI path; it's ignored on the legacy fallback.

Using with React Navigation — native bar (createLiquidGlassTabNavigator)

If you want the genuine iOS 26 liquid glass tab bar wired up to React Navigation, use createLiquidGlassTabNavigator. It's a 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',          // selected tab tint (bar-wide)
          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>
  );
}

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, override per-screen. | | tabBarMinimizeBehavior | 'automatic' \| 'never' \| 'onScrollDown' \| 'onScrollUp' | Scroll-driven minimize behavior. iOS 26 only; ignored on the legacy fallback.|

What renders per iOS version

| Version | Bar | | ---------------- | ------------------------------------------------------------------- | | 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 row (LiquidGlassTabViewFallback) |

What tabBarTintColor colors

  • iOS 26: selected tab's icon + label tint (UIView.tintColor on the SwiftUI host).
  • 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 — liquid glass reads from whatever is behind it. Put a colored view behind your screens if you want the bar to look tinted.

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

Using with React Navigation — custom bar (LiquidGlassTabBar)

If you're using @react-navigation/bottom-tabs and want the custom draggable droplet bar instead of the native one, pass LiquidGlassTabBar as the tabBar prop. React Navigation handles routing, focus, deep linking, and screen lifecycles — LiquidGlassTabBar just renders the bar UI.

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:

  • The mapping is straightforward: state.routestabs, state.indexactiveIndex, and tab presses are forwarded through React Navigation's navigate (with the tabPress event so screens can preventDefault — used for "scroll to top on tap" patterns).
  • Pull tabBarIcon, tabBarLabel, tabBarAccessibilityLabel from each route's descriptor.options so users can configure tabs from <Tab.Screen> like they normally would.
  • Set tabBarStyle: { display: 'none' } is not needed — React Navigation only renders one tab bar (the one you pass via tabBar).
  • For the screens not to be hidden behind the floating bar, give your screens paddingBottom: 100 (or use useSafeAreaInsets and add the bar height).

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 to the next tab | | animation | { type: 'spring' \| 'timing' \| 'none'; config?: ... } | spring (iOS-like) | How the pill animates between tabs. Spring / timing configs are passed through to Animated | | pillBorderRadius | number | 999 | Active-pill border radius. Default is fully rounded; lower for a more rectangular droplet | | effect | 'clear' \| 'regular' \| 'none' | 'regular' | Glass effect of the active pill. The bar uses clear to keep the pill visually distinct | | 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 underlying LiquidGlassContainerView merge effect | | style | StyleProp<ViewStyle> | — | Outer wrapper style — use to position (e.g. absolute at the bottom) | | tabStyle | StyleProp<ViewStyle> | — | Style for each tab's Pressable | | activeTabStyle | StyleProp<ViewStyle> | — | Extra style for the active pill (merged with the animated transform) |

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

The package also exports the building blocks if you want to compose your own glass UIs:

import {
  LiquidGlassView,
  LiquidGlassContainerView,
  isLiquidGlassSupported,
} 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) — the "previous liquid glass" look
  • Use isLiquidGlassSupported to detect the genuine iOS 26 path; use isLegacyGlassSupported to detect any iOS (legacy or new). Both are false on Android.

Credits

Built on top of @callstack/liquid-glass by Callstack — the underlying UIGlassEffect Fabric component is theirs. This package adds a higher-level LiquidGlassTabBar with drag, ripple, and configurable animations.

License

MIT

Support the project 💛

If this saved you time and you'd like to contribute, you can support the project on Gumroad — every bit helps keep open-source maintenance going.

👉 kkcreek.gumroad.com/l/dqnhp

A ⭐ on the GitHub repo is also hugely appreciated — it helps others discover the project.