Skip to content

Instantly share code, notes, and snippets.

@dutradotdev
Last active May 31, 2024 02:16
Show Gist options
  • Save dutradotdev/50c82763fc621ab3c1bd5ba02180ce0d to your computer and use it in GitHub Desktop.
Save dutradotdev/50c82763fc621ab3c1bd5ba02180ce0d to your computer and use it in GitHub Desktop.
BlurContainer in React Native
import React from 'react';
import WebView from 'react-native-webview';
export type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`;
export interface BlurContainerProps {
backgroundColor: RGBA;
blurRadius: number;
}
const BlurContainer = ({ backgroundColor, blurRadius }: BlurContainerProps) => {
return (
<WebView
style={[
tw('absolute inset-0 bg-white/0'),
{ backgroundColor: '#00000000' },
]}
source={{
html: `
<div style="position: absolute;
top: 0;
right:0;
bottom: 0;
left: 0;
background: ${backgroundColor};
-webkit-backdrop-filter: blur(${blurRadius}px);
backdrop-filter: blur(${blurRadius}px);"
/>
`,
}}
/>
);
};
export default BlurContainer;
@dutradotdev
Copy link
Author

Usage

In the same View, the first element will be behind of BlurContainer and the third will be over BlurContainer.

BlurContainer has the size of parent View.

import BlurContainer from '<your-path>';
import { Dimensions, Image, View, Text } from 'react-native';

const page = () => {
  const windowWidth = Dimensions.get('window').width;

  return (
    <View style={tw('h-[306px]')}>
      <Image
        source={{ uri: 'https://picsum.photos/id/237/200/300' }}
        style={tw(`w-[${windowWidth}px] h-[400px] absolute inset-0 z-0`)}
      />
      <BlurContainer backgroundColor="rgba(240, 68, 56, 0.1)" blurRadius={10} />
      <View style={tw('absolute inset-0 border')}>
        <Text style={tw('text-white')}>testing 01</Text>
        <Text style={tw('text-white')}>testing 02</Text>
      </View>
    </View>
  );
};

About this approach

I did research to find out how to implement blur effects in View. React Native doesn’t have an easy way to do this, only for images.

Approach:

Use ImageBackground + transparent .png and blurRadius prop to create a blur container

Result: Do not work for both platforms.

Approach:

Use react-native-blur to do the blur
Cons: Lib with a lot of bugs, issues and build issues. Not a performatic approach.
Positioning elements do not work very well
References:
Kureev/react-native-blur#483
Kureev/react-native-blur#395

Approach:

Use expo-blur

Pros: Works very well for web and iOS

Cons: Do not work for android, it renders a semi-transparent View. We need to install an expo in the storybook in order to be able to build storybook

Approach:

Use react-native-skia

Result: Works only for images, you can’t use react-native elements inside <Canvas>

Approach:

Use react-native-svg

Result: Do not work. we don’t have the tags Svg, Defs, Filter.

Approach:

Use react-native-reanimated

Result: We can’t play with blur w/ reanimated, the tag backdrop filter works only for some iOS devices.

Approach:

Use react-native-linear-gradient

Result: We can play only w/ transparency, not with blur

Approach:

Use react-native-webview and inject pure CSS to generate a View with blur

Result: Works on both platforms.

@DinoZenka
Copy link

What does tw function do? Can you share example with native styles, please?

@dutradotdev
Copy link
Author

@DinoZenka the tw function is for tailwind. Using native styles:

Blur container component:

<WebView
      style={[
        { position: 'absolute', top: 0, bottom:0, left:0, right: 0, backgroundColor: '#00000000' },
      ]}
      source={{
        html: `
            <div style="position: absolute;
            top: 0;
            right:0;
            bottom: 0;
            left: 0;
            background: ${backgroundColor};
            -webkit-backdrop-filter: blur(${blurRadius}px);
            backdrop-filter: blur(${blurRadius}px);"
            />
      `,
      }}
    />
  );

usage:

import BlurContainer from '<your-path>';
import { Dimensions, Image, View, Text } from 'react-native';

const page = () => {
  const windowWidth = Dimensions.get('window').width;

  return (
    <View style={{ height: 306 }}>
      <Image
        source={{ uri: 'https://picsum.photos/id/237/200/300' }}
        style={{ width: windowWidth, height: 400, position: 'absolute', top:0, bottom:0, left:0, right: 0, zIndex:0 }
      />
      <BlurContainer backgroundColor="rgba(240, 68, 56, 0.1)" blurRadius={10} />
      <View style={{position: 'absolute', top:0, bottom:0, left:0, right: 0, borderWidth: 1}}>
        <Text style={{ textColor: 'white' ]}>testing 01</Text>
        <Text style={{ textColor: 'white' ]}>testing 02</Text>
      </View>
    </View>
  );
};

@crank-chips
Copy link

crank-chips commented Mar 15, 2023

Great solution. Though there is an issue with Android. The blurred background is flickering on scrolling (see attachment). Is there a way to fix it?
iOS works perfectly fine.

UPD:
Also, it doesn't work on the physical device, it only works on Android Emulator for some reason.

Screen.Recording.2023-03-14.at.5.04.34.PM.mov

@Aboa123
Copy link

Aboa123 commented Mar 17, 2023

If the height is too small on Android 13, the blur effect does not apply.

import BlurContainer from '<your-path>';
import { Dimensions, Image, View, Text } from 'react-native';

const page = () => {
  const windowWidth = Dimensions.get('window').width;

  return (
     <View style={{ height: 120  }}>
      <BlurContainer backgroundColor="rgba(240, 68, 56, 0.1)" blurRadius={10} />
     </View>
  );
};

In this code, the blur effect was ignored for Android.

import React from "react";
import { Dimensions, View } from "react-native";
import WebView from "react-native-webview";

export type RGBA = `rgba(${number}, ${number}, ${number}, ${number})`;

export interface BlurContainerProps {
	backgroundColor: RGBA;
	blurRadius: number;
}

const BlurContainer: React.FC<BlurContainerProps> = ({ backgroundColor, blurRadius }) => {
	return (
	<View
		style={{
			position: "absolute",
			top: 0,
			bottom: 0,
			left: 0,
			right: 0,
			backgroundColor: "#00000000",
			overflow: "hidden"
		}}
	>
		<WebView
			style={{
				width: "100%",
				height: "100%",
				backgroundColor: "#00000000"
			}}
			originWhitelist={["*"]}
			overScrollMode='never'
			scrollEnabled={false}
			source={{
				html: `
          <html>
            <head>
              <meta name="viewport" content="initial-scale=1.0 maximum-scale=1.0" />
              <style>
                .blur {
                  position: absolute;
                  top: 0;
                  right:0;
                  bottom: 0;
                  left: 0;
                  height: ${Dimensions.get("screen").height}px;
                  background: ${backgroundColor};
                  -webkit-backdrop-filter: blur(${blurRadius}px);
                  backdrop-filter: blur(${blurRadius}px);
                }
              </style>
            </head>
            <body>
              <div class="blur" />
            </body>
          </html>
        `
				}}
			/>
		</View>
	);
};

export default BlurContainer;

i'm sorry the code line.
the important thing is the height.

height: ${Dimensions.get("screen").height}px;

@sayanMidknightStudio
Copy link

Hi , can anyone help me ? There is a problem , when I using this this with border Radius , the blur does not appear.

Screenshot_20231012_215115_Talentverse
Screenshot_20231012_215213_Talentverse

@shav95
Copy link

shav95 commented Dec 9, 2023

@sayanMidknightStudio

import {View} from 'react-native';
import WebView from 'react-native-webview';

export type RGBA = rgba(${number}, ${number}, ${number}, ${number});

export interface BlurContainerProps {
backgroundColor: RGBA;
blurRadius: number;
borderRadius: number;
containerBorderWidth?: number;
}

const BlurContainer = ({
backgroundColor,
blurRadius,
borderRadius,
containerBorderWidth = 1,
}: BlurContainerProps) => {
return (
<View
style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}>
<WebView
containerStyle={{}}
style={[
{
backgroundColor: '#00000000',
},
]}
originWhitelist={['*']}
overScrollMode="never"
scrollEnabled={false}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
source={{
html: `



<style>

            * {
              padding: 0;
              margin: 0;
            }
            .blur{
              position: absolute;
              width: calc(100% - ${containerBorderWidth}px);
              height: calc(100% - ${containerBorderWidth}px);
              background: ${backgroundColor};
              -webkit-backdrop-filter: blur(${blurRadius}px);
              backdrop-filter: blur(${blurRadius}px);
              border-radius: ${borderRadius}px;
            }
          </style>
        </head>
        <body>
          <div class="blur" />
        </body>
      </html>`,
    }}
  />
</View>

);
};

export default BlurContainer;

Try this solution. It helped me.

And also notice this containerBorderWidth property if you have border in your React Native View you should subtract it from div width and height

@Aboa123
Copy link

Aboa123 commented Dec 12, 2023

@sayanMidknightStudio

<View style={{ borderRadius: 16, overFlow: "hidden" }}>
   <BlurView />
</View>

how about use like this

@sayanMidknightStudio
Copy link

@Aboa123 @shav95 i did the same and all types of modification.... but every time i add borderRadious the is vanished

@Aboa123
Copy link

Aboa123 commented Dec 20, 2023

@sayanMidknightStudio can you show me ur code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment