npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rn-blur-overlay

v2.0.1

Published

GPU-accelerated Gaussian blur for React Native — content blur and true backdrop blur (glassmorphism), with Skia-equivalent sigma and no Skia dependency. iOS uses real-time QuartzCore layer filters over UIVisualEffectView; Android 12+ uses RenderEffect on

Readme

<BlurView blur={20}>{content}</BlurView>                        // blur the children
<BlurView mode="backdrop" blur={20} />                          // blur what's behind
<BlurView mode="backdrop" blur={30} fadeDirection="down" />      // ramp it off

Why this exists

Most React Native blur libraries hand you an opaque 0–100 "intensity" dial. A Figma spec of backdrop-blur: 10.85px becomes a number someone tunes by eye — to a different value on each platform. Several also snapshot their backdrop once per frame on the main thread: fine for one static chip, ruinous on a scrolling list.

rn-blur-overlay takes a different position on both:

  • blur is a Gaussian sigma in DP — the same unit as react-native-skia's <Blur blur={σ} /> and CSS backdrop-filter: blur(). A Figma value transfers verbatim, and iOS and Android agree.
  • The default paths never snapshot. iOS drives QuartzCore layer filters; Android 12+ drives a RenderEffect on a RenderNode. Both recompute on the platform's own render thread against a live display list, so content scrolling underneath updates the blur for free.

Install

npm install rn-blur-overlay
cd ios && pod install

That is the whole setup. Autolinked on both platforms — no MainApplication edit, no manual ReactPackage registration, no Gradle changes.

New Architecture note. The native side is legacy view managers, which Fabric renders through its interop layer. That layer is on by default and needs no configuration — but it is explicitly transitional in React Native's own source. Fabric-native components are on the roadmap.


Quick start

Content blur — blur the children

Blurs this view's own subtree. Any children: images, text, nested containers, icons, gradients, or any mix of them. Nothing branches on child type.

import { BlurView } from 'rn-blur-overlay';
import { Image, StyleSheet, View } from 'react-native';

export const BlurredCover = ({ uri }: { uri: string }) => (
  <View style={styles.frame}>
    <BlurView blur={20}>
      <Image source={{ uri }} style={StyleSheet.absoluteFill} resizeMode="cover" />
    </BlurView>
  </View>
);

const styles = StyleSheet.create({
  frame: { width: 300, height: 200, overflow: 'hidden' },
});

Content mode fills its parent by default — see rule 3 if you want it to size to its children instead.

Backdrop blur — frosted glass

Wrap what should be blurred in <BlurTarget>, then place the <BlurView> as its sibling.

import { BlurTarget, BlurView } from 'rn-blur-overlay';
import { ScrollView, StyleSheet, Text, View } from 'react-native';

export const GlassBar = () => (
  <View style={styles.root}>
    <BlurTarget style={StyleSheet.absoluteFill}>
      <ScrollView>{/* … */}</ScrollView>
    </BlurTarget>

    <BlurView mode="backdrop" blur={24} tint="#FFFFFF33" saturation={1.8} style={styles.bar}>
      <Text style={styles.label}>Stays sharp on top of the blur</Text>
    </BlurView>
  </View>
);

const styles = StyleSheet.create({
  root: { flex: 1 },
  bar: {
    position: 'absolute', left: 16, right: 16, bottom: 32,
    height: 88, borderRadius: 24, justifyContent: 'center', paddingHorizontal: 20,
  },
  label: { color: '#FFFFFF', fontWeight: '600' },
});

Children of a backdrop BlurView draw on top of the blur, unblurred. That is how you get crisp labels on a glass panel.

Progressive blur — ramp it off

Fully blurred at one edge, sharp at the other. Works in both modes.

<BlurView
  mode="backdrop"
  blur={30}
  fadeDirection="down"   // blurred at the top, sharp at the bottom
  fadeStart={0.3}        // hold full blur through the first 30%
  fadeEnd={1}            // fully sharp by the bottom edge
  style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 140 }}
/>

The sigma scale

blur is σ, not a percentage. Rules of thumb for picking one:

| σ | Reads as | Typical use | |:--:|---|---| | 24 | a faint frost | de-emphasised text, disabled surfaces, subtle depth | | 612 | clear glass | nav bars, tab bars, floating toolbars, chips | | 1624 | heavy glass | modal scrims, sheets, cards over photography | | 30+ | fully obscured | privacy screens, app-switcher masking, splash transitions |

Two boundaries worth knowing:

  • Below σ 0.5 there is no blur on Android 12+. RenderEffect's radius 0 already yields σ 0.5, so smaller values round away. Use blurEnabled={false} to mean "off".
  • Above σ 10.6 Android 7–11 downsamples. One RenderScript pass caps at radius 25 (σ ≈ 10.6), so larger sigmas are reached by blurring a smaller bitmap — softer and slightly less precise there.

Engines

The engine is picked automatically from mode × platform × fidelity × whether a fade or tint is set. You never choose one — this is what each costs.

| Mode | Platform / condition | Engine | σ | Per-frame cost | |---|---|---|:--:|---| | backdrop | iOS 13.4+ · fidelity="exact" (default) | CAFilter gaussianBlur on the UIVisualEffectView backdrop layer | exact | none — render server recomputes | | backdrop | iOS 13.4+ · fidelity="public" | UIVisualEffectView + paused UIViewPropertyAnimator | approx. | none | | backdrop | Android 12+ · with BlurTarget | RenderEffect over the target's live display list | exact | none — one GPU pass, no readback | | backdrop | Android 12+ · no BlurTarget | same, plus a re-record of the window root | exact | one extra tree traversal | | backdrop | Android 7–11 | RenderScript over a downscaled snapshot | close | one capture when content or position changes | | content | iOS 13.4+ · exact, no fade | CAFilter gaussianBlur on the view's own layer | exact | none — render server recomputes | | content | iOS 13.4+ · public, or any fade | CIGaussianBlur on a Metal CIContext | exact | one capture per layout or child change | | content | Android 12+ · no fade, no tint | RenderEffect on the view itself | exact | none — hwui blurs the layer | | content | Android 12+ · fade or tint · and Android 7–11 | children re-recorded into a RenderNode, or a RenderScript bitmap below API 31 | exact / close | one re-record (or one bitmap pass) per draw |

Two paths snapshot instead of sampling live. It is worth knowing which:

  • Android 7–11 draws the target into a downscaled bitmap and blurs it with RenderScript. It re-captures solely when the target's content or the blur view's position actually changed.
  • iOS content blur with a fade, or with fidelity="public", snapshots the subtree through Core Image. Capture happens on the main thread; the blur itself runs on a background queue. It refreshes on layout changes and child insertions — so animating or video children can go stale. Drop the fade (or keep fidelity="exact") and iOS blurs live instead.

Three rules that matter

Everything else is optional. These three are not.

1. A BlurView must be a sibling of its BlurTarget, never a descendant

// ✅ sibling — samples the target's subtree, and nothing else
<View>
  <BlurTarget style={StyleSheet.absoluteFill}>{photo}</BlurTarget>
  <BlurView mode="backdrop" blur={20} style={panel}>{label}</BlurView>
</View>

// ⚠️ nested — still renders, but the target has to be re-recorded every frame
<BlurTarget>
  {photo}
  <BlurView mode="backdrop" blur={20} />
</BlurTarget>

This is what scopes the sample. Get it wrong and it still looks correct at first, which is what makes it worth stating: a nested BlurView falls back to re-recording its enclosing BlurTarget, and with no BlurTarget anywhere in the tree it re-records the window root. Both are a second traversal per frame, and on Android sampling the window root means a panel can blur its own children into a halo behind them. Android logs the reason once per view to logcat under the RNBlurView tag.

BlurTarget cannot capture SurfaceView-backed content — video, maps, GL. TextureView works on Android 12+.

2. A backdrop blur paints over its container's backgroundColor

The blur draws the sampled backdrop across the whole view, so a fill underneath it is hidden. Put the fill on tint, and keep the container colour only as a fallback for when the blur cannot draw (a software canvas — e.g. a programmatic screenshot — falls back to tint alone).

// ❌ the blur covers this fill — the panel reads transparent
<View style={{ backgroundColor: '#FFFFFFCC' }}>
  <BlurView mode="backdrop" blur={12} style={StyleSheet.absoluteFill} />
</View>

// ✅ tint carries the fill; the container keeps it as a fallback
<View style={{ backgroundColor: '#FFFFFFCC' }}>
  <BlurView mode="backdrop" blur={12} tint="#FFFFFFCC" style={StyleSheet.absoluteFill} />
</View>

3. Content mode fills its parent

mode="content" has to cover the children it blurs, so it defaults to position: absolute on all four edges. That is what makes <BlurView blur={20}><Image style={StyleSheet.absoluteFill} /></BlurView> work without sizing anything by hand.

It is a default, not a constraint — your style wins. To use it as an ordinary in-flow container that hugs its children:

<BlurView blur={10} style={{ position: 'relative', padding: 16 }}>
  <Text>Sized by its own content, blurred in place</Text>
</BlurView>

mode="backdrop" gets no positioning default at all — it is a panel with its own place in the layout.


API

<BlurView>

Accepts every standard View prop, plus:

| Prop | Type | Default | Description | |---|---|---|---| | blur | number \| { x, y } | — | Required. Gaussian sigma (σ) in DP/points. Same unit as Skia and CSS. 0 disables. Pass { x, y } for an anisotropic blur. | | mode | 'content' \| 'backdrop' | 'content' | Blur the children, or blur what is behind. | | tint | ColorValue | — | Colour composited over the blur. Use a translucent one — see rule 2. | | saturation | number | 1 | 0 = greyscale, 1.52 = Apple-material punch. | | tileMode | 'clamp' \| 'repeat' \| 'mirror' \| 'decal' | 'clamp' | How the kernel samples past the edges. Android only — Core Image and CAFilter always clamp. | | fadeDirection | 'up' \| 'down' \| 'left' \| 'right' | — | Enables progressive blur, fading out in this direction. | | fadeStart | number | 0 | Where the ramp starts, 0–1. Fully blurred before it. | | fadeEnd | number | 1 | Where the ramp ends, 0–1. Fully sharp after it. | | blurEnabled | boolean | true | Switch the blur off without unmounting or rebuilding the native pipeline. | | autoUpdate | boolean | true | Keep tracking the content behind. Only read on the Android backdrop paths that re-record — always on 7–11, and on 12+ when there is no BlurTarget. Ignored on the BlurTarget fast path and on iOS. | | downsampleFactor | number | auto | Snapshot downscale, used as a floor. Android 7–11 only. | | fidelity | 'exact' \| 'public' | 'exact' | See iOS fidelity. iOS only. | | material | BlurMaterial | — | Apple material tint — thin, chrome, dark, ultraThin, … iOS mode="backdrop" only. | | cornerRadii | [tl, tr, br, bl] | from style | Overrides the radii read from style. |

borderRadius (and the per-corner variants) are read from style and applied to the blur layer itself, so <BlurView style={{ borderRadius: 24 }} /> rounds the blur — not just the border.

<BlurTarget>

Plain View props. Marks the subtree that a sibling BlurView samples.

On Android it records its children into a RenderNode as part of its normal draw, so the blur costs one GPU pass and no extra UI-thread work — and it also lets the blur view know when the content actually changed, which is what keeps the pre-Android-12 path from re-capturing needlessly. On iOS it is a plain view: UIVisualEffectView already samples everything composited beneath it. Keeping it in both trees means one JSX tree behaves identically on both platforms.

setDefaultBlurFidelity(fidelity)

import { setDefaultBlurFidelity } from 'rn-blur-overlay';

setDefaultBlurFidelity('public'); // opt the whole app out of the private-API path

Call once at startup, before anything renders. Views already mounted keep the value they were created with.

Types

BlurViewProps, BlurTargetProps, BlurMode, BlurTileMode, BlurFadeDirection, BlurFidelity, and BlurMaterial are all exported.


Recipes

<View style={{ flex: 1 }}>
  <BlurTarget style={StyleSheet.absoluteFill}>
    <FlatList data={items} renderItem={renderItem} contentContainerStyle={{ paddingBottom: 96 }} />
  </BlurTarget>

  <BlurView
    mode="backdrop"
    blur={18}
    tint="rgba(255,255,255,0.18)"
    saturation={1.8}
    style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 88, paddingBottom: 24 }}
  >
    <Tabs />
  </BlurView>
</View>
<BlurView
  mode="backdrop"
  blur={28}
  fadeDirection="down"
  fadeStart={0.45}
  style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 120 }}
  pointerEvents="none"
/>

Full blur behind the status bar, dissolving into sharp content by the bottom edge.

<BlurTarget style={StyleSheet.absoluteFill}>
  <Screen />
</BlurTarget>

<BlurView
  mode="backdrop"
  blur={24}
  tint="rgba(0,0,0,0.35)"
  saturation={0.85}
  style={StyleSheet.absoluteFill}
>
  <Sheet />
</BlurView>
<View style={{ height: 240, overflow: 'hidden' }}>
  <BlurView blur={16}>
    <Image source={{ uri }} style={StyleSheet.absoluteFill} resizeMode="cover" />
  </BlurView>

  {/* sibling, not a child — so it stays sharp */}
  <Text style={styles.title}>Now Playing</Text>
</View>

In content mode the children are the thing being blurred, so anything that must stay crisp goes outside the BlurView.

const [sigma, setSigma] = useState(0);
// …drive with Animated/Reanimated on the JS side, or just setState
<BlurView mode="backdrop" blur={sigma} tint="#FFFFFF22" style={panel} />

Cheap on iOS exact and Android 12+ (a property change on an existing filter). Expensive on Android 7–11, where every value change means a fresh capture and RenderScript pass — animate a tint's opacity there instead.

<BlurView mode="backdrop" blur={20} autoUpdate={false} style={panel} />

Captures once, then holds. Useful for a static backdrop behind a modal on older Android, where staying live costs a capture whenever the content moves.


iOS fidelity

iOS has no public API for "blur the backdrop by exactly σ" — UIBlurEffect offers a handful of fixed materials with no radius knob. So there are two paths.

'exact' (default) drives QuartzCore's real-time layer filters, the same mechanism UIVisualEffectView uses internally. blur becomes a true Gaussian sigma matching Android and Skia, and the render server recomputes it — nothing is snapshotted. This reaches CAFilter, which is not public API. The mitigations: symbol names are base64-encoded so they never appear as literals in the binary, resolved once at runtime, every lookup nil-checked, and any failure degrades to the public path on its own. The same technique backs Flutter's BackdropFilter on iOS.

'public' uses public API only: a UIVisualEffectView material whose intensity is scrubbed with a paused UIViewPropertyAnimator — the approach expo-blur takes. blur is then approximate rather than an exact sigma, mapped onto 0–1 over roughly σ 0–40. Content blur under 'public' goes through Core Image, which is an exact sigma, but snapshotted.

Switch per view with fidelity, or app-wide with setDefaultBlurFidelity. Android is unaffected either way — every Android path is public framework API.


Performance

  • On iOS exact and Android 12+, the blur is recomputed by the platform's render server or RenderThread. No bitmaps per frame, nothing on the JS thread.
  • Android 7–11 backdrop blur is the path to measure. If you support these versions and need blur on a scrolling surface, check dumpsys gfxinfo before and after — or fall back to a translucent tint there.
  • Always add a BlurTarget for backdrop blur. Without one, every frame costs a second traversal of the window root.
  • Don't stack many backdrop blurs. On iOS, more than ~5 simultaneous UIVisualEffectViews measurably hurts compositing.
  • Prefer blurEnabled={false} over unmounting when toggling — it keeps the native pipeline built.
  • Android logs ViewManagerPropertyUpdater: Could not find generated setter … at startup. Benign: props fall back to reflection because the package ships no annotation-processor output yet. See Roadmap.

Troubleshooting

| Symptom | Cause | Fix | |---|---|---| | Panel reads transparent, no glass | The blur painted over the container's backgroundColor | Move the fill to tintrule 2 | | Blur looks right but scrolling is janky on old Android | Android 7–11 snapshot path | autoUpdate={false}, raise downsampleFactor, or use a tint below API 31 | | Video / map / GL content isn't blurred | SurfaceView composites outside the view's display list | Use a TextureView mode (Android 12+) if the library offers one | | Panel's own children show as a halo behind them | No BlurTarget, so the window root — including the panel — is being sampled | Add a BlurTarget sibling — rule 1 | | Nothing blurs at very low blur values | σ ≤ 0.5 rounds to no blur on Android 12+ | Use σ ≥ 1, and blurEnabled={false} for "off" | | iOS content blur is stale over animating children | Fade or fidelity="public" put it on the Core Image snapshot path | Drop fadeDirection, or keep fidelity="exact" | | borderRadius clips the view but not the blur | Radii come from style; a computed or overridden shape may not resolve | Pass cornerRadii={[tl, tr, br, bl]} explicitly | | Blur missing in a programmatic screenshot | Software canvas can't draw a RenderNode | Expected — it degrades to tint alone; keep a container backgroundColor as backup | | iOS blur looks like a fixed Apple material, not your σ | CAFilter lookup failed, so it degraded to the public path | Verify fidelity isn't set to 'public'; the fallback is by design |


Upgrading from 1.x

  • blur now means the same sigma on both platforms. 1.x applied a hand-tuned σ × 2.2 radius on Android with no density conversion, so Android over-blurred relative to iOS — by a different amount per screen density. Expect Android to be somewhat softer at the same value now, and consistent with iOS. Re-tune anything you calibrated on Android.
  • Existing usage keeps working. mode defaults to content, and content mode still fills its parent.
  • BlurTarget and mode="backdrop" are new. Nothing existing needs to change to adopt them.
  • Children of a BlurView are now laid out by React Native. Earlier versions let the underlying FrameLayout re-position them, stacking every child at the top-left.

Roadmap

  • Fabric-native components. Today's view managers reach the New Architecture through RN's interop layer, which upstream marks as transitional.
  • Generated prop setters, to drop the reflection fallback and its startup log.
  • Android 7–11: evaluate retiring the RenderScript path (deprecated since API 31) in favour of a tint fallback.

Credits

The Android backdrop architecture follows the approach proven by Dimezis/BlurView. Sigma parity is based on hwui's Blur::convertRadiusToSigma (σ = 0.57735 × radius + 0.5) and RenderScript's σ = 0.4 × radius + 0.6. The iOS backdrop-layer filter technique is the same one used by aheze/VariableBlurView, dominicstop/VisualEffectBlurView, and Flutter's iOS engine.

License

MIT © Parthil Savaliya