Build a Liquid Glass UI with Expo GlassEffect
expo-glass-effect gives Expo apps access to native Liquid Glass-style surfaces. Here is a practical way to use it without pretending every platform can render the same effect.
The short version
Use expo-glass-effect for carefully scoped iOS Liquid Glass surfaces: headers, cards, floating controls, and compact panels. Guard it at runtime and ship a normal React Native fallback for Android, web, older iOS, and accessibility settings.
The important caveat is support. Expo documents GlassView as available on iOS 26 and above, with a fallback to a regular View on unsupported platforms. That makes it exciting, but not something to wrap your whole app in without checks.
What GlassEffect is
expo-glass-effect exposes React components that render a glass effect using iOS native visual effect APIs. In practice, that means you can create UI that feels like the new system glass language instead of faking it with translucent views and blur hacks.
The package is bundled with Expo SDK 56 and is included in Expo Go, so it is easy to try. The production decision is less casual: this is a platform feature with platform limits, and those limits should be visible in your component design.
Use it as an enhancement
Treat glass as progressive enhancement. The app should remain readable and usable when the glass API is missing, reduced, or intentionally disabled.
1. Install GlassEffect
Install the package through Expo so the native package version matches your SDK.
npx expo install expo-glass-effect
If you are starting from scratch, create an Expo app first, then install the package.
npx create-expo-app@latest glass-demo
cd glass-demo
npx expo install expo-glass-effect
2. Create a safe glass wrapper
Start with a wrapper that can decide whether to render GlassView or a normal View. This keeps the risky checks in one place instead of spreading platform conditions through every screen.
import { useEffect, useState } from "react";
import { AccessibilityInfo, Platform, StyleSheet, View } from "react-native";
import {
GlassView,
isGlassEffectAPIAvailable,
isLiquidGlassAvailable,
} from "expo-glass-effect";
export function useCanRenderGlass() {
const [canRenderGlass, setCanRenderGlass] = useState(false);
useEffect(() => {
let mounted = true;
async function checkSupport() {
const reduceTransparency =
await AccessibilityInfo.isReduceTransparencyEnabled();
const supported =
Platform.OS === "ios" &&
isGlassEffectAPIAvailable() &&
isLiquidGlassAvailable() &&
!reduceTransparency;
if (mounted) {
setCanRenderGlass(supported);
}
}
checkSupport();
const subscription = AccessibilityInfo.addEventListener(
"reduceTransparencyChanged",
checkSupport
);
return () => {
mounted = false;
subscription.remove();
};
}, []);
return canRenderGlass;
}
export function SafeGlassPanel({ children, style }) {
const canRenderGlass = useCanRenderGlass();
if (!canRenderGlass) {
return <View style={[styles.fallbackPanel, style]}>{children}</View>;
}
return (
<GlassView
style={[styles.glassPanel, style]}
glassEffectStyle="regular"
tintColor="rgba(255,255,255,0.22)"
isInteractive>
{children}
</GlassView>
);
}
const styles = StyleSheet.create({
glassPanel: {
borderRadius: 24,
overflow: "hidden",
padding: 18,
},
fallbackPanel: {
borderRadius: 24,
padding: 18,
backgroundColor: "rgba(15,23,42,0.86)",
borderWidth: StyleSheet.hairlineWidth,
borderColor: "rgba(255,255,255,0.18)",
},
});
isGlassEffectAPIAvailable() is the crash-prevention check. Expo added it because some iOS 26 beta states may report Liquid Glass components while the underlying API is not safe to use. isLiquidGlassAvailable() tells you whether the app can use the Liquid Glass component family.
3. Build a practical glass header
Use the wrapper for a contained, high-impact surface. A header card is a good first target because it benefits from material depth, but the app still works if the fallback renders instead.
import { ImageBackground, StyleSheet, Text, View } from "react-native";
import { SafeGlassPanel } from "./SafeGlassPanel";
export function DestinationHeader() {
return (
<ImageBackground
source={require("../assets/coast.jpg")}
style={styles.hero}
imageStyle={styles.heroImage}>
<View style={styles.overlay}>
<SafeGlassPanel style={styles.card}>
<Text style={styles.kicker}>Weekend plan</Text>
<Text style={styles.title}>Galway coast walk</Text>
<Text style={styles.body}>
Native glass on supported iOS devices. Clear, readable fallback
everywhere else.
</Text>
</SafeGlassPanel>
</View>
</ImageBackground>
);
}
const styles = StyleSheet.create({
hero: {
minHeight: 360,
justifyContent: "flex-end",
},
heroImage: {
borderRadius: 28,
},
overlay: {
padding: 20,
},
card: {
gap: 8,
},
kicker: {
color: "#a7f3d0",
fontWeight: "800",
textTransform: "uppercase",
},
title: {
color: "#ffffff",
fontSize: 30,
fontWeight: "800",
},
body: {
color: "rgba(255,255,255,0.84)",
lineHeight: 22,
},
});
Keep text contrast high. Glass is a background treatment, not a reason to make the content harder to read.
4. Group glass surfaces with GlassContainer
When multiple glass views sit near each other, wrap them in GlassContainer. That gives the native effect more context and keeps related glass surfaces behaving like a small system.
import { GlassContainer } from "expo-glass-effect";
import { StyleSheet, Text, View } from "react-native";
import { SafeGlassPanel, useCanRenderGlass } from "./SafeGlassPanel";
export function FloatingControls() {
const Container = useCanRenderGlass() ? GlassContainer : View;
return (
<Container style={styles.container}>
<SafeGlassPanel style={styles.control}>
<Text style={styles.value}>22C</Text>
<Text style={styles.label}>Weather</Text>
</SafeGlassPanel>
<SafeGlassPanel style={styles.control}>
<Text style={styles.value}>4.8 km</Text>
<Text style={styles.label}>Route</Text>
</SafeGlassPanel>
</Container>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: "row",
gap: 12,
},
control: {
minWidth: 120,
},
value: {
color: "#ffffff",
fontSize: 22,
fontWeight: "800",
},
label: {
color: "rgba(255,255,255,0.72)",
},
});
The grouped container uses the same runtime guard. If glass is unavailable, the wrapper falls back to a normal View while the child panels keep their readable fallback styling.
5. Handle platform and accessibility fallbacks
A good fallback is not a dull afterthought. It should be part of the design. For unsupported platforms, render an opaque or lightly translucent card with a border. For users who reduce transparency, prefer clarity over decoration.
- iOS 26+: render
GlassViewonly after the runtime checks pass. - Older iOS: use the same layout with a readable fallback view.
- Android and web: do not imply Liquid Glass support; use normal React Native styling.
- Reduced transparency: respect the accessibility setting and avoid glass-heavy UI.
The production rule
Never gate critical actions behind a glass-only surface. If the effect disappears, the screen should still explain itself and every button should still be reachable.
Final checklist
- Install with
npx expo install expo-glass-effect. - Use
GlassViewfor contained surfaces, not entire app shells. - Use
GlassContainerwhen nearby glass surfaces should behave as a group. - Guard rendering with
isGlassEffectAPIAvailable()andisLiquidGlassAvailable(). - Respect
AccessibilityInfo.isReduceTransparencyEnabled(). - Test the fallback path on Android, web, older iOS, and reduced transparency settings.