@askewxyz/react-native-liquid-glass
v0.3.1
Published
Apple Liquid Glass (UIGlassEffect + UIGlassContainerEffect) for React Native via Expo Modules. Includes HIG-aligned components: Button, Toolbar, Sidebar, Sheet, and a content-layer Card.
Readme
react-native-liquid-glass
Apple Liquid Glass (UIGlassEffect, UIGlassContainerEffect) for React Native, exposed via an Expo Module. Built strictly against Apple's iOS 26 UIKit API and the Human Interface Guidelines for materials.
Requirements
- iOS 26+ for true
UIGlassEffect. On older iOS, the view falls back toUIBlurEffect(.systemUltraThinMaterial)/.systemMaterial. - Xcode 26+ (iOS 26 SDK).
- Expo SDK 54+, React Native 0.81+, React 19+.
- A development build (Expo Go cannot load custom native modules).
Install
npx expo install @askewxyz/react-native-liquid-glass
npx expo prebuild --platform ios
cd ios && pod install
npx expo run:iosAPI surface
Primitives (thin wrappers over UIKit)
<LiquidGlassView /> wraps UIVisualEffectView(effect: UIGlassEffect(style: ...)).
| Prop | Type | Maps to |
| ------------- | -------------------------- | ----------------------------- |
| variant | 'regular' \| 'clear' | UIGlassEffect.Style |
| tintColor | ColorValue | UIGlassEffect.tintColor |
| interactive | boolean | UIGlassEffect.isInteractive |
| cornerRadius| number | layer corner radius |
<LiquidGlassContainer /> wraps UIVisualEffectView(effect: UIGlassContainerEffect()). Nest <LiquidGlassView> instances inside it and they will be reparented into the container's contentView so iOS morphs their shapes fluidly into each other (per Apple's container effect docs).
Components (HIG-aligned)
<GlassButton />- mirrors SwiftUI.buttonStyle(.glass)/.glassProminent. Props:label,variantStyle,variant,tintColor,icon,iconStyle,labelStyle,height,cornerRadius(defaultheight/2, always a pill),contentPaddingHorizontal,disabled,onPress,onLongPress,accessibilityLabel,accessibilityHint,testID,style.<GlassNavBar />- floating top navigation bar. Props:title,leading,trailing(both accept any ReactNode),variant,tintColor,height(default 56),cornerRadius(defaultheight/2),titleStyle,contentPaddingHorizontal,accessibilityLabel,testID,style.<GlassToolbar />- floating tab bar / toolbar. Items use aLiquidGlassContainerso they morph together. Props:items,height,itemHeight,itemCornerRadius,activeTintColor,gap,paddingHorizontal,variant,labelStyle,iconStyle,style,accessibilityLabel,testID. Each item:key,icon,label?,onPress?,onLongPress?,active?,tintColor?,accessibilityLabel?,testID?.<GlassSidebar />- vertical navigation column. Props:items,width,itemHeight,itemCornerRadius,activeTintColor,gap,paddingVertical,paddingHorizontal,variant,labelStyle,iconStyle,header,footer,style,accessibilityLabel,testID. Each item: same shape as toolbar plus a requiredlabel.<GlassSheet />- modal sheet withdetent: 'half' | 'full'. Props:visible,onDismiss,detent,variant,tintColor,cornerRadius,backdropOpacity(default 0.35),backdropColor,disableBackdropDismiss,hideHandle,springFriction,contentPaddingHorizontal,contentPaddingBottom,style,contentStyle,accessibilityLabel,testID.<ContentCard />- intentionally NOT Liquid Glass. Props:title?,body?,eyebrow?,children,backgroundColor,cornerRadius,padding,titleStyle,bodyStyle,eyebrowStyle,style,accessibilityLabel,testID. Per Apple HIG, Liquid Glass belongs to the navigation/control layer; cards in the content layer should use standard materials.
When to use what
Apple's HIG, paraphrased:
- Glass = functional layer (bars, sidebars, controls, sheets). Use
variant="regular"(default) for surfaces with text. Usevariant="clear"over media and add a ~35% dark dimming layer behind if the media is bright. - Glass != content layer. Don't put articles, list rows, or feed cards behind glass.
- Group multiple glass elements with a
LiquidGlassContainer(we wrapUIGlassContainerEffectfor this). Don't stack glass on glass; let neighbouring shapes morph. - Use color sparingly. Tint glass only to emphasize a primary action.
Example
import {
GlassButton,
GlassNavBar,
GlassToolbar,
GlassSidebar,
GlassSheet,
ContentCard,
} from '@askewxyz/react-native-liquid-glass';
export default function Screen() {
const [open, setOpen] = useState(false);
return (
<>
<GlassNavBar
title="Inbox"
leading={<GlassButton label="Back" icon="<" />}
trailing={<GlassButton label="Edit" variantStyle="glassProminent" />}
/>
<ContentCard title="In the content layer" body="Standard material, not glass." />
<GlassButton label="Open" variantStyle="glassProminent" onPress={() => setOpen(true)} />
<GlassToolbar
items={[{ key: 'h', icon: 'H', label: 'Home', active: true }]}
activeTintColor="rgba(0,122,255,0.55)"
/>
<GlassSheet
visible={open}
onDismiss={() => setOpen(false)}
backdropOpacity={0.5}
hideHandle={false}
>
...
</GlassSheet>
</>
);
}Web fallback
A pure-DOM fallback renders backdrop-filter: blur(...) saturate(...) so layouts don't crash when targeting web. Visual fidelity is best-effort.
Repo layout
react-native-liquid-glass/
index.ts <- public API
src/ <- library TS sources
LiquidGlassView.tsx
LiquidGlassContainer.tsx
components/
ios/ <- Swift: UIGlassEffect / UIGlassContainerEffect
android/ <- Kotlin: no-op stubs
__tests__/ <- jest-expo, 24 tests
expo-module.config.json
package.json
example/ <- demo app that consumes the library via file:..
App.tsx
package.jsonRun the demo
git clone https://github.com/askew-xyz/react-native-liquid-glass.git
cd react-native-liquid-glass
npm install
cd example && npm install
npx expo prebuild --platform ios
cd ios && pod install && cd ..
npx expo run:iosTesting
npm testJest with the jest-expo preset. The native view is mocked via requireNativeView so tests do not need a built native binary.
License
MIT.
