react-native-liquid-pill-tabbar
v0.5.0
Published
Zero-native-dependency, Apple-Liquid-Glass-inspired UI kit for React Native — floating pill tab bar, FAB, search bar, header, chips, segmented control, badges, bottom sheet, toast, modal, and progress bar, all sharing one translucent glass look and one br
Maintainers
Readme
react-native-liquid-pill-tabbar
A React Native component kit with an Apple Liquid Glass–inspired feel — translucent surfaces, springy pill motion, zero native modules. Works on iOS, Android, and web.
Started as a floating pill tab bar; now a full glass UI kit (search bar, sheets, modals, toasts, chips, and more) that all share the same look and the same brand color.
Why
Apple's Liquid Glass isn't a public API and has no Android equivalent. This kit fakes the effect convincingly with Animated and view layering only — no Skia, no native glass, no linking required to get started.
How the glass effect works
- One shared pill, not one per item. The active pill is a single
Animated.Viewthat springs to position/width — it travels between items instead of popping. - Springs, not linear easing — position and width use slightly offset spring configs, so width settles a beat after position. That lag reads as squash-and-stretch.
- Depth from layering, not rendering tricks: translucent fill, hairline border, soft shadow. Optional real backdrop blur if you install a blur package (see below) — completely optional, everything looks good without it.
Install
npm install react-native-liquid-pill-tabbar react-native-safe-area-contextreact-native-safe-area-context is a required peer dependency (safe-area insets for the tab bar and header).
Quick start — tab bar
import { BottomTabBar, TabItem } from 'react-native-liquid-pill-tabbar';
import { Home, ShoppingBag } from 'lucide-react-native';
const TABS: TabItem[] = [
{ key: 'home', label: 'Home', renderIcon: ({ color, size }) => <Home color={color} size={size} /> },
{ key: 'shop', label: 'Shop', renderIcon: ({ color, size }) => <ShoppingBag color={color} size={size} /> },
];
<BottomTabBar tabs={TABS} activeTab={activeTab} onTabPress={setActiveTab} colorScheme="light" />Icons are bring-your-own (renderIcon) — vector-icons, lucide, Expo icons, SVGs, whatever you use. active in the render callback lets you swap outline/filled variants.
Your brand color, once
Wrap your app root in GlassThemeProvider to set your brand color and color scheme once — every component in the kit picks it up automatically:
import { GlassThemeProvider } from 'react-native-liquid-pill-tabbar';
<GlassThemeProvider tintColor="#9663A2" colorScheme="dark">
{/* your app tree */}
</GlassThemeProvider>tintColor and colorScheme are both optional — omit either to keep that component's built-in default. No provider at all? Everything works exactly like before (fully backward compatible).
Override per-instance when needed. Any component can still accept an explicit tintColor or colorScheme prop that takes precedence over the provider value:
<GlassThemeProvider tintColor="#9663A2">
{/* All chips use #9663A2 by default */}
<GlassChip label="Default tint" ... />
{/* This one uses a different brand color — local prop wins */}
<GlassChip label="Custom tint" tintColor="#FF6B6B" ... />
</GlassThemeProvider>For the tab bar specifically, use theme for per-tab-bar styling (the tab bar uses its own TabBarTheme, not tintColor):
<BottomTabBar theme={{ activePillBg: '#9663A2', activePillText: '#FFFFFF' }} ... />tintColor accepts hex (#RGB/#RRGGBB) or rgb()/rgba(). In dev, an unrecognized format warns instead of failing silently.
Real blur (optional)
Looks good with flat translucency alone. For true backdrop blur, install a blur library and inject it once in the provider — the package never imports one directly, so pick whichever fits your project:
# Expo
npx expo install expo-blur expo-linear-gradient
# Bare RN CLI
npm install @react-native-community/blur react-native-linear-gradient && cd ios && pod installimport { BlurView } from 'expo-blur'; // or '@react-native-community/blur'
import { GlassThemeProvider } from 'react-native-liquid-pill-tabbar';
// Set blur once — every component inherits it
<GlassThemeProvider tintColor="#9663A2" colorScheme="dark" BlurView={BlurView} blurType="dark">
{/* All components now have real backdrop blur */}
</GlassThemeProvider>You can still override per-component by passing BlurView/blurAmount/blurType/LinearGradient as individual props — the local prop always wins over the provider value.
Hide tab bar on scroll (optional)
import { TabBarScrollProvider, useTabBarHideOnScroll } from 'react-native-liquid-pill-tabbar';
function ProductList() {
return <FlatList {...useTabBarHideOnScroll()} data={products} renderItem={...} />;
}
<TabBarScrollProvider>{/* screens + <BottomTabBar /> */}</TabBarScrollProvider>No provider = bar just stays visible, no crash.
Component kit
All components share colorScheme, optional palette override, and optional BlurView/LinearGradient injection.
| Component | What it is |
|---|---|
| GlassSurface | Base primitive — translucent fill/border/shadow. Build custom glass with it. |
| GlassFAB | Circular floating action button. |
| GlassSearchBar | Collapses to an icon, springs open into a text field on focus. |
| GlassHeader | Inline top bar with safe-area handling, title, leading/trailing slots. |
| GlassChip | Filter/tag pill — compose several in a ScrollView for a filter row. |
| GlassSegmentedControl | Multi-option toggle with a shared sliding pill. |
| GlassBadge | Count/dot pill — position it yourself via style. |
| GlassBottomSheet | Draggable sheet (handle-drag or backdrop-tap to dismiss). |
| GlassToast | Auto-dismissing confirmation pill. |
| GlassModal | Centered dialog, fade+scale, flexible footer slot. |
| GlassProgressBar | Thin tinted progress track — determinate or indeterminate. |
import { GlassFAB, GlassSearchBar, GlassBottomSheet, GlassModal, GlassProgressBar } from 'react-native-liquid-pill-tabbar';
<GlassFAB renderIcon={...} onPress={...} tintColor="#9663A2" />
<GlassSearchBar value={query} onChangeText={setQuery} expandedWidth={280} />
<GlassBottomSheet visible={open} onClose={() => setOpen(false)} snapHeight={0.45}>
<Text>Sheet content</Text>
</GlassBottomSheet>
<GlassModal visible={open} onClose={() => setOpen(false)} title="Delete item?"
footer={<Button title="Confirm" onPress={confirm} />}>
<Text>This cannot be undone.</Text>
</GlassModal>
<GlassProgressBar progress={0.6} tintColor="#9663A2" />Tab bar props reference
| Prop | Type | Default | Description |
|---|---|---|---|
| tabs | TabItem[] | required | key, label, renderIcon, optional accessibilityLabel |
| activeTab | string | required | Key of the active tab |
| onTabPress | (key: string) => void | required | Tap handler |
| theme | Partial<TabBarTheme> | — | surface, activePillBg, activePillText, inactiveText, shadowColor |
| colorScheme | 'light' \| 'dark' | 'light' | Built-in glass palette |
| hideOnScroll | boolean | true | Needs TabBarScrollProvider |
| iconSize | number | 22 | Passed to renderIcon |
| BlurView / LinearGradient | component | — | Optional injected blur/gradient |
| blurAmount | number | 24 | Passed to BlurView |
| blurType | string | 'light' | Passed to BlurView (bare RN) |
License
MIT
