← Zurück zum Blog

Liquid Glass in React Native Expo — So setzt du es richtig ein

Liquid Glass in React Native Expo — So setzt du es richtig ein

Mit iOS 26 hat Apple Liquid Glass eingeführt — ein dynamischer Glasmorphism-Effekt, der sich in Echtzeit an den Hintergrund anpasst. Für React Native Entwickler stellt sich die Frage: Wie nutze ich das in meiner Expo-App? In diesem Guide zeige ich dir die komplette Implementierung mit Expo SDK 55, inklusive einem 3-Tier-Fallback-System für ältere iOS-Versionen und Android.

Voraussetzungen

Expo SDK 55 bringt alles mit, was du brauchst: expo-glass-effect für die native Liquid Glass Komponente (GlassView), expo-blur als Fallback für ältere iOS-Versionen, expo-symbols für SF Symbols Icons, und expo-router mit nativer Liquid Glass Integration.

Schritt 1: Platform Detection

Der wichtigste Baustein: Eine Utility-Funktion, die erkennt, ob Liquid Glass verfügbar ist. Diese Funktion ist die Basis für das gesamte Fallback-System.

utils/platform.ts
import { Platform } from 'react-native';export function isIOS26OrLater(): boolean {  if (Platform.OS !== 'ios') return false;  return parseInt(Platform.Version as string, 10) >= 26;}

Schritt 2: Das 3-Tier-Fallback-System

Tier 1 (iOS 26+): Natives Liquid Glass via GlassView. Tier 2 (iOS < 26): BlurView mit systemChromeMaterial. Tier 3 (Android): Opaker Fallback mit Background-Color. Dieses System stellt sicher, dass deine App auf jedem Gerät gut aussieht.

Schritt 3: GlassCard — Container mit Glaseffekt

Die GlassCard ist die Basis-Komponente für alle glasartigen Container. Wichtig: overflow: 'hidden' ist Pflicht — ohne das rendert der Effekt über die Grenzen der Komponente hinaus. Bei BlurView muss der Content zIndex: 1 haben, damit er über dem Blur-Layer erscheint.

components/ui/glass-card.tsx
import { GlassView } from 'expo-glass-effect';import { BlurView } from 'expo-blur';import { Platform, StyleSheet, View } from 'react-native';import { isIOS26OrLater } from '@/utils/platform';export function GlassCard({ children, style, tintColor }) {  // Tier 1: iOS 26+ — native Liquid Glass  if (isIOS26OrLater()) {    return (      <GlassView        glassEffectStyle="regular"        tintColor={tintColor}        style={[styles.card, style]}>        {children}      </GlassView>    );  }  // Tier 2: iOS < 26 — BlurView  if (Platform.OS === 'ios') {    return (      <View style={[styles.card, style]}>        <BlurView          tint="systemChromeMaterial"          intensity={80}          style={StyleSheet.absoluteFill}        />        <View style={{ zIndex: 1 }}>{children}</View>      </View>    );  }  // Tier 3: Android — opaque fallback  return (    <View style={[styles.card,      { backgroundColor: '#1a1a1a' }, style]}>      {children}    </View>  );}const styles = StyleSheet.create({  card: {    borderRadius: 20,    padding: 16,    overflow: 'hidden',  },});

Schritt 4: GlassIconButton — Interaktive Glass-Buttons

Auf iOS 26+ übernimmt isInteractive die native Bounce-Animation. Für ältere Versionen baust du sie mit Reanimated nach.

components/ui/glass-icon-button.tsx
import { GlassView } from 'expo-glass-effect';import { SymbolView } from 'expo-symbols';import { Pressable, StyleSheet, View } from 'react-native';import Animated, {  useSharedValue, useAnimatedStyle,  withTiming, Easing} from 'react-native-reanimated';import { isIOS26OrLater } from '@/utils/platform';const AnimatedPressable =  Animated.createAnimatedComponent(Pressable);export function GlassIconButton({ name, onPress, size = 20 }) {  const scale = useSharedValue(1);  const animatedStyle = useAnimatedStyle(() => ({    transform: [{ scale: scale.value }],  }));  const handlePressIn = () => {    scale.value = withTiming(0.82, {      duration: 120, easing: Easing.out(Easing.ease),    });  };  const handlePressOut = () => {    scale.value = withTiming(1, {      duration: 200, easing: Easing.out(Easing.back(1.5)),    });  };  const icon = (    <SymbolView name={name} tintColor="#fff"      size={size} weight="semibold" />  );  // iOS 26+: isInteractive handles the bounce  if (isIOS26OrLater()) {    return (      <Pressable onPress={onPress}>        <GlassView glassEffectStyle="regular"          isInteractive style={styles.button}>          {icon}        </GlassView>      </Pressable>    );  }  // Fallback: Reanimated scale animation  return (    <AnimatedPressable      onPressIn={handlePressIn}      onPressOut={handlePressOut}      onPress={onPress}      style={animatedStyle}>      <View style={[styles.button,        { backgroundColor: 'rgba(255,255,255,0.1)' }]}>        {icon}      </View>    </AnimatedPressable>  );}const styles = StyleSheet.create({  button: {    width: 44, height: 44, borderRadius: 999,    alignItems: 'center', justifyContent: 'center',    overflow: 'hidden',  },});

Schritt 5: Header-Toolbar mit Glass-Buttons

Expo-router bietet unstable_headerRightItems und unstable_headerLeftItems für custom Header-Items. Setze headerBlurEffect auf undefined auf iOS 26+, damit natives Liquid Glass den Effekt übernimmt. hidesSharedBackground: true verhindert doppelte Glass-Hintergründe.

app/(tabs)/home/_layout.tsx
import { Stack } from 'expo-router';import { Platform } from 'react-native';import { GlassIconButton } from '@/components/ui/glass-icon-button';import { isIOS26OrLater } from '@/utils/platform';export default function HomeLayout() {  return (    <Stack>      <Stack.Screen        name="index"        options={{          title: 'Übersicht',          headerTransparent: Platform.OS === 'ios',          headerBlurEffect: isIOS26OrLater()            ? undefined : 'dark',          unstable_headerRightItems: () => [{            type: 'custom',            element: (              <GlassIconButton                name="bell.fill"                onPress={() => router.push('/notifications')}              />            ),            hidesSharedBackground: true,          }],          unstable_headerLeftItems: () => [{            type: 'custom',            element: (              <GlassIconButton                name="gearshape.fill"                onPress={() => router.push('/settings')}              />            ),            hidesSharedBackground: true,          }],        }}      />    </Stack>  );}

Schritt 6: Tab Bar mit NativeTabs

Die Tab Bar bekommt Liquid Glass automatisch mit NativeTabs. Auf iOS 26+ rendert NativeTabs automatisch eine Tab Bar mit Liquid Glass Effekt — kein zusätzlicher Code nötig.

components/app-tabs.tsx
import { NativeTabs } from 'expo-router/unstable-native-tabs';export default function AppTabs() {  return (    <NativeTabs      labelVisibilityMode="unlabeled"      iconColor={{ selected: '#EA580C' }}>      <NativeTabs.Trigger name="home">        <NativeTabs.Trigger.Label hidden />        <NativeTabs.Trigger.Icon          sf={{ default: 'house', selected: 'house.fill' }} />      </NativeTabs.Trigger>      <NativeTabs.Trigger name="explore">        <NativeTabs.Trigger.Label hidden />        <NativeTabs.Trigger.Icon          sf={{ default: 'safari', selected: 'safari.fill' }} />      </NativeTabs.Trigger>      <NativeTabs.Trigger name="profile" role="search">        <NativeTabs.Trigger.Icon          sf={{ default: 'person', selected: 'person.fill' }} />      </NativeTabs.Trigger>    </NativeTabs>  );}

Schritt 7: Farbiges Liquid Glass

Für einen Highlight-Button mit farbigem Glass-Effekt: tintColor mit reduzierter Opacity (BF = 75% als Hex-Suffix) gibt einen subtilen Farbton, ohne die Transparenz zu zerstören.

Farbiges Liquid Glass
<Pressable onPress={handleAction}>  {isIOS26OrLater() ? (    <GlassView      glassEffectStyle="regular"      isInteractive      tintColor={`${accentColor}BF`}      style={styles.ctaButton}>      <SymbolView name="sparkles" size={18}        tintColor="#fff" weight="semibold" />      <Text style={styles.label}>Jetzt starten</Text>    </GlassView>  ) : (    <View style={[styles.ctaButton, styles.fallback]}>      <BlurView tint="dark" intensity={60}        style={StyleSheet.absoluteFill} />      <View style={{ zIndex: 1, flexDirection: 'row',        alignItems: 'center', gap: 8 }}>        <SymbolView name="sparkles" size={18}          tintColor="#fff" weight="semibold" />        <Text style={styles.label}>Jetzt starten</Text>      </View>    </View>  )}</Pressable>

Schritt 8: Stack-Screens konfigurieren

Für jeden Stack-Screen das gleiche Pattern — egal ob Settings, Detail-Ansicht oder Modal.

Stack.Screen Pattern
<Stack.Screen  name="settings"  options={{    title: 'Einstellungen',    presentation: 'card',    headerBackTitle: 'Zurück',    headerTransparent: Platform.OS === 'ios',    headerBlurEffect: isIOS26OrLater()      ? undefined : 'regular',  }}/>

Die wichtigsten Regeln

1. Immer overflow: 'hidden' — ohne das rendert GlassView über die Komponentengrenzen hinaus. 2. isInteractive nur für klickbare Elemente. 3. headerBlurEffect: undefined auf iOS 26+ — sonst kollidiert Legacy-Blur mit Liquid Glass. 4. hidesSharedBackground: true — verhindert doppelte Glass-Hintergründe bei custom Header-Items. 5. zIndex: 1 über BlurView — Content verschwindet sonst hinter dem Blur-Layer. 6. Alle drei Tiers implementieren — nicht jeder User hat iOS 26.

Fazit

Liquid Glass in React Native mit Expo SDK 55 ist überraschend einfach — wenn du das Pattern einmal verstanden hast. Bau dir eine GlassCard und einen GlassIconButton als Basis-Komponenten, und du kannst Liquid Glass konsistent durch deine gesamte App einsetzen. Der Effekt ist subtil, aber er macht einen spürbaren Unterschied — deine App fühlt sich sofort wie eine native iOS 26 App an.

Noch mehr lesen?

Alle Beiträge →