Description
BlurView does not render the blur effect on first mount when:
- Using
StyleSheet.absoluteFillObject (no explicit dimensions)
- Parent dimensions are determined by dynamic content (e.g., Text)
- Rendered inside a FlashList (or FlatList with aggressive recycling)
After scrolling away and back (triggering view recycling), the blur renders correctly.
Environment
@sbaiahmed1/react-native-blur: 4.2.2
- React Native: 0.79.2
- Expo SDK: 54
- iOS: 18.x
- Device: iPhone 15 Pro (Simulator and physical)
Reproduction
import { BlurView } from '@sbaiahmed1/react-native-blur';
import { FlashList } from '@shopify/flash-list';
import { StyleSheet, Text, View } from 'react-native';
const PLACEHOLDER_TEXT = "This content is exclusive to pass holders...";
function LockedPostCard() {
return (
<View style={{ position: 'relative', marginTop: 12 }}>
{/* Dynamic text determines parent height */}
<Text numberOfLines={4}>{PLACEHOLDER_TEXT}</Text>
{/* BlurView with absoluteFillObject - FAILS on first render */}
<BlurView
blurType="light"
blurAmount={8}
style={StyleSheet.absoluteFillObject}
/>
</View>
);
}
// Rendered in FlashList
<FlashList
data={posts}
renderItem={({ item }) => <LockedPostCard post={item} />}
/>
Expected Behavior
Blur should render immediately on first mount.
Actual Behavior
- First render: No blur visible (content behind is fully visible)
- After scrolling away and back: Blur renders correctly
Root Cause Analysis
After investigating the iOS native code, the issue appears to be when the blur effect is initialized relative to the view lifecycle.
@sbaiahmed1/react-native-blur Implementation
File: ios/Views/AdvancedBlurView.swift
@objc public class AdvancedBlurView: UIView {
public override init(frame: CGRect) {
super.init(frame: frame)
setupHostingController() // ← Blur set up immediately in init()
}
public override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview != nil {
setupHostingController() // ← Called again, but still too early
}
}
}
The SwiftUI hosting controller (and blur effect) is set up in init() — before the view is added to the hierarchy and before sibling views have rendered.
When using absoluteFillObject, the BlurView relies on parent dimensions. If the parent's dimensions come from dynamic content (Text layout), those dimensions may not be finalized when the blur captures its initial state.
Comparison with expo-blur
File: expo-blur/ios/BlurEffectView.swift
final class BlurEffectView: UIVisualEffectView {
override func draw(_ rect: CGRect) { // ← Blur applied in draw()
super.draw(rect)
effect = nil
animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in
self.effect = visualEffect
}
animator?.fractionComplete = CGFloat(intensity)
}
}
expo-blur applies the blur effect in draw(_:), which is part of UIKit's rendering cycle. This method is called by the system only when the view is ready to be displayed — meaning layout is complete and sibling views have rendered.
Timeline Comparison
┌─────────────────────────────────────────────────────────────────────────┐
│ @sbaiahmed1/react-native-blur │
├─────────────────────────────────────────────────────────────────────────┤
│ init() → setupHostingController() called │
│ addSubview() → View added to hierarchy │
│ layoutSubviews() → Layout calculated │
│ [Sibling content renders] │
│ ❌ Blur already initialized with incorrect/zero dimensions │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ expo-blur │
├─────────────────────────────────────────────────────────────────────────┤
│ init() → View created (no blur yet) │
│ addSubview() → View added to hierarchy │
│ layoutSubviews() → Layout calculated │
│ [Sibling content renders] │
│ draw(_:) → Blur applied NOW │
│ ✅ Layout complete, dimensions known, blur renders correctly │
└─────────────────────────────────────────────────────────────────────────┘
Why It Works in Other Cases
The issue does not occur when:
| Scenario |
Why it works |
Explicit width/height provided |
Dimensions known at init time |
| Blurring static bundled images |
Image already loaded, dimensions fixed |
| Not in FlashList |
Less aggressive timing, more layout cycles |
Workarounds
1. Delay rendering (not ideal - causes flash of unblurred content)
const [blurReady, setBlurReady] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setBlurReady(true), 100);
return () => clearTimeout(timer);
}, []);
{blurReady && <BlurView ... />}
2. Use explicit dimensions (not always possible with dynamic content)
<BlurView
style={{
position: 'absolute',
width: 300,
height: 100,
}}
/>
Suggested Fix
Consider deferring the blur effect setup to draw(_:) or layoutSubviews() (after calling super.layoutSubviews()) to ensure parent dimensions and sibling content are finalized before capturing the blur.
Alternatively, the UIViewRepresentable implementation could use updateUIView more effectively to re-apply the blur after the initial layout pass.
Additional Context
- This issue is reproducible in both Simulator and physical devices
- The 100ms delay workaround confirms this is a timing issue
Description
BlurViewdoes not render the blur effect on first mount when:StyleSheet.absoluteFillObject(no explicit dimensions)After scrolling away and back (triggering view recycling), the blur renders correctly.
Environment
@sbaiahmed1/react-native-blur: 4.2.2Reproduction
Expected Behavior
Blur should render immediately on first mount.
Actual Behavior
Root Cause Analysis
After investigating the iOS native code, the issue appears to be when the blur effect is initialized relative to the view lifecycle.
@sbaiahmed1/react-native-blurImplementationFile:
ios/Views/AdvancedBlurView.swiftThe SwiftUI hosting controller (and blur effect) is set up in
init()— before the view is added to the hierarchy and before sibling views have rendered.When using
absoluteFillObject, the BlurView relies on parent dimensions. If the parent's dimensions come from dynamic content (Text layout), those dimensions may not be finalized when the blur captures its initial state.Comparison with
expo-blurFile:
expo-blur/ios/BlurEffectView.swiftexpo-blurapplies the blur effect indraw(_:), which is part of UIKit's rendering cycle. This method is called by the system only when the view is ready to be displayed — meaning layout is complete and sibling views have rendered.Timeline Comparison
Why It Works in Other Cases
The issue does not occur when:
width/heightprovidedWorkarounds
1. Delay rendering (not ideal - causes flash of unblurred content)
2. Use explicit dimensions (not always possible with dynamic content)
Suggested Fix
Consider deferring the blur effect setup to
draw(_:)orlayoutSubviews()(after callingsuper.layoutSubviews()) to ensure parent dimensions and sibling content are finalized before capturing the blur.Alternatively, the
UIViewRepresentableimplementation could useupdateUIViewmore effectively to re-apply the blur after the initial layout pass.Additional Context