The basic BlurView
npx expo install expo-blurimport { BlurView } from 'expo-blur';
import { ImageBackground, StyleSheet, Text } from 'react-native';
export default function FrostedCard() {
return (
<ImageBackground source={require('./photo.jpg')} style={styles.bg}>
<BlurView intensity={60} tint="dark" style={styles.card}>
<Text style={styles.title}>Golden hour</Text>
<Text style={styles.meta}>Lisbon · 12 photos</Text>
</BlurView>
</ImageBackground>
);
}
const styles = StyleSheet.create({
bg: { flex: 1, justifyContent: 'flex-end', padding: 16 },
card: {
padding: 20,
borderRadius: 20,
overflow: 'hidden', // without this the corners stay square
},
title: { color: '#fff', fontSize: 20, fontWeight: '600' },
meta: { color: 'rgba(255,255,255,0.7)', marginTop: 4 },
});intensity runs from 1 to 100 and defaults to 50. tint adds a translucent colour layer on top of the blur: light, dark, default, and on iOS the system materials such as systemThinMaterialthat match Apple’s own controls. There is no tint that renders the blur alone, so if a dark tint looks muddy, lower the intensity rather than hunting for a “none” option. Children of the BlurView render sharp on top of the blur, which is why the text above stays readable.
Why Android shows a flat tint, and the fix
| iOS | Android | Web | |
|---|---|---|---|
| Real blur by default | ✅ Yes | ❌ Semi-transparent view | ✅ backdrop-filter |
| Needs BlurTargetView | No | Yes, for a real blur | No |
| blurMethod prop | Ignored | none, dimezisBlurView, dimezisBlurViewSdk31Plus | Ignored |
| Performance cost | Low | Higher, best on Android 12+ | Browser dependent |
| Expo Go | ✅ Yes | ✅ Yes | n/a |
iOS has a system blur that any view can use cheaply. Android does not, so expo-blur ships a blur implementation that is off by default and renders a semi-transparent view instead. To turn it on, mark the content that should be blurred with BlurTargetView and point the BlurView at it:
import { useRef } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { BlurTargetView, BlurView } from 'expo-blur';
export default function Screen({ children }) {
const targetRef = useRef<View | null>(null);
return (
<View style={{ flex: 1 }}>
<BlurTargetView ref={targetRef} style={StyleSheet.absoluteFill}>
{children}
</BlurTargetView>
<BlurView
blurTarget={targetRef} // Android: what to blur
blurMethod="dimezisBlurViewSdk31Plus"
intensity={70}
tint="dark"
style={styles.bar}
>
<Text style={{ color: '#fff' }}>Now playing</Text>
</BlurView>
</View>
);
}
const styles = StyleSheet.create({
bar: { position: 'absolute', left: 16, right: 16, bottom: 32, padding: 16, borderRadius: 16, overflow: 'hidden' },
});dimezisBlurViewSdk31Plus uses the real blur on Android 12 and newer and falls back to the tint on older devices, which avoids a noticeable performance hit on old phones. dimezisBlurView blurs everywhere at that cost. iOS ignores both props, so the same code is safe on both platforms. If you are on an older Expo SDK, the prop was called experimentalBlurMethod and there was no BlurTargetView, so check the docs for the SDK version in your package.json before copying examples.
Blurred tab bars and headers
The most common use of expo-blur is a translucent tab bar that content scrolls under. With Expo Router or React Navigation bottom tabs, the bar has to be absolutely positioned so content can pass behind it, and the blur goes in tabBarBackground:
import { Tabs } from 'expo-router';
import { BlurView } from 'expo-blur';
import { Platform, StyleSheet, View } from 'react-native';
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarStyle: { position: 'absolute', borderTopWidth: 0, backgroundColor: 'transparent' },
tabBarBackground: () =>
Platform.OS === 'ios' ? (
<BlurView tint="systemChromeMaterialDark" intensity={80} style={StyleSheet.absoluteFill} />
) : (
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'rgba(18,18,18,0.94)' }]} />
),
}}
/>
);
}The Android branch is a deliberate choice. A real Android blur needs the screen content wrapped in a BlurTargetView, which the tab navigator does not do for you, and a nearly opaque bar looks at home on Android anyway. Because the bar is now absolute, every screen must add bottom padding equal to the tab bar height, or the last list item hides behind it. useBottomTabBarHeight() from React Navigation gives you that number, and the tab bar guide covers the inset maths.
For headers on iOS you may not need expo-blur at all. The native stack supports headerTransparent: true with headerBlurEffect: 'systemChromeMaterial', which uses the real UIKit navigation bar blur and handles large titles correctly. Use a BlurView header only when you need the same look on Android or a custom header layout.
Two traps that make a blur look wrong
- Square corners on a rounded card. A
borderRadiuson BlurView is not enough. Addoverflow: 'hidden'or the blur is drawn to the square edge of the box. - The blur freezes over a list. If the BlurView is rendered before dynamic content such as a FlatList in the component tree, the blur may not update as the list scrolls. Render the BlurView after the content it sits over, which also matches how absolutely positioned overlays should be ordered anyway.
Keep blurred areas small. A full-screen blur over a scrolling list costs more on every frame, especially on Android. A blurred bar, a card, or a bottom sheet backdrop is where the effect earns its cost. For a static background, a gradient often gives a similar feel for almost nothing.
Blur, glass, or neither
On iOS 26 and later, expo-glass-effectgives you Apple’s Liquid Glass material through GlassView. It falls back to a plain View on Android and older iOS, so treat it as an iOS enhancement, with expo-blur or a solid colour as the cross-platform baseline. And if the content under the bar is mostly flat colour, a blur adds cost without any visible effect, so a translucent solid is the better choice.
A blur is impossible to judge in a static mockup because the whole point is the content moving behind it. Describe the screen, “a photo grid with a blurred floating tab bar”, and ShipNative builds it as real React Native that runs on your phone, so you can scroll under the bar and decide whether it earns its place.