iOS Liquid Glass
Native glass via expo-glass-effect with capability gates and solid fallbacks.
Native iOS 26 Liquid Glass via expo-glass-effect — FABs, pills, cards,
toolbars, popovers. Never rebuilt with blur + opacity. Expo iOS only: Android
and web render the solid fallback, which is the same shape by construction.
When to use
- Floating controls, pills, cards, toolbars, and popovers on iOS where the system glass material is available.
- Anchors for native dropdown menus (
MenuView) on device builds.
When not to use
- Android or web surfaces — they get the fallback, so design the fallback first and treat glass as enhancement.
- Every
View— glass is for floating/elevated controls, not backgrounds. - SwiftUI (
liquid-glass-designskill) or@expo/uimenus (expo-uiskill) — different owners.
File map
src/theme/glass.ts # NATIVE_GLASS / NATIVE_MENU capability flags
src/components/glass-*.tsx # glass control + same-shape solid fallback
npx expo install expo-glass-effect expo-blur expo-symbols
# only when using native menus:
npx expo install @react-native-menu/menu
Full procedure: the expo-ios-glass skill (mount gates, GlassContainer
merging, header bars, popovers, motion budgets).
Key excerpts (adapted)
Gate on the capability API — never on isLiquidGlassAvailable(), never ANDed
(iOS 26 betas can crash), and never && the transparency promise:
import { Platform, UIManager } from 'react-native';
import Constants from 'expo-constants';
import { isGlassEffectAPIAvailable } from 'expo-glass-effect';
export const NATIVE_GLASS = Platform.OS === 'ios' && isGlassEffectAPIAvailable();
function nativeMenuAvailable(): boolean {
if (Platform.OS === 'web') return false;
// Expo Go never ships MenuView — a real device/dev-client binary does.
if (Constants.appOwnership === 'expo') return false;
try {
return UIManager.hasViewManagerConfig('MenuView');
} catch {
return false;
}
}
export const NATIVE_MENU = nativeMenuAvailable();
Glass or same-shape fallback — "regular" for controls, "clear" for
popovers only, isInteractive only on tappables, theme-driven colorScheme:
{NATIVE_GLASS ? (
<GlassView glassEffectStyle="regular" isInteractive={tappable} colorScheme={scheme} style={s.pill}>
{children}
</GlassView>
) : (
<View style={[s.pill, { backgroundColor: colors.card }]}>{children}</View>
)}
Live demo
Open the (patterns)/glass-card route in the showcase/ app: a glass pill
and card under the capability gate, the live NATIVE_GLASS readout, and the
solid fallback each renders off-platform. Expo Go is enough (note: MenuView
is intentionally not demoed — Expo Go has no such native view).
Anti-patterns
- Fake glass:
rgbafill +opacityon a plainView. - Mounting on
isLiquidGlassAvailable()(alone or ANDed with the capability API) — beta crash risk. tintColoras an rgba “token” — fills are theme solids;tintColortakes the resolved theme color.Ioniconswhereexpo-symbolsSymbolViewbelongs;zeegowhereMenuViewbelongs.- Unguarded
MenuViewin Expo Go — redUnimplemented componentbox. Gate onNATIVE_MENU, with anActionSheetIOSfallback on iOS Go and an expandable list on Android/web. - Opacity or
FadeInanimations onGlassViewor its parents; motion budget is 180–240ms cubic on non-glass only, honoring Reduce Motion. - Hardcoded
colorScheme="dark"—"auto"for system-following, otherwise the app’s in-app toggle value.
Provenance
Application labels are anonymized because the production source checkouts are private. They document observed SDK versions and file shapes, not publicly reproducible sources.
- External
expo-ios-glassskill (full procedure this page condenses). - Production Expo 57 app B
src/theme/glass.ts:NATIVE_GLASS/NATIVE_MENUgates. - Production Expo 57 app C
DESIGN.md§3–§5: glass token table, surface hierarchy, and thecolorScheme-binding + no-washed-gray invariants.