All components
0:00/0:00
Appearance

Recorded on iPhone. A tilted wall of paintings drifts under a soft blur at the status bar and a deep frost behind the onboarding copy.

Blur

Progressive Blur

Content frosts over gradually toward any edge of the screen, with no hard line where the blur starts.

Features

  • Your content fades softly into a blur at the edge of the screen, with no hard line
  • Blur the top, the bottom, the sides, or all four at once
  • Keep the screen sharp until someone scrolls, then let the blur build
  • Blend it into your background so buttons and text stay easy to read
  • Works on lists, photos, videos and web pages
  • The real system blur on iPhone and Android, and smooth while you scroll

Built with

  • Expo native module
  • iOS: UIVisualEffectView (Swift, Objective-C)
  • Android: RenderEffect (Kotlin)
  • Reanimated

Early access

Checkout opens soon

TesseraUI is in early access. Leave your email and we'll let you know the moment you can buy, before anyone else.

No spam. One email when checkout opens.

Or get it with Pro

Installation

1Install dependencies
npx expo install react-native-reanimated react-native-worklets
2Run a development build (Expo Go can't load native modules)
npx expo run:ios   # or: npx expo run:android

Usage

import { ProgressiveBlur } from '@/modules/progressive-blur';

export function Feed() {
  return (
    <ProgressiveBlur top style={{ flex: 1 }}>
      <ScrollView>{/* … */}</ScrollView>
    </ProgressiveBlur>
  );
}

With nothing else set, the top 110 points blur. The same code runs on iOS and Android.

Platforms

It's a native module with its own code for each platform, so the blur is the platform's own, not a JavaScript imitation.

iOSAndroid
SupportediOS 16.4 and laterAndroid 12 (API 31) and later for the blur; Android 7–11 shows the content sharp, with the tint
BlurUIKit's system blur (UIVisualEffectView), the one under iOS's own barsAndroid's RenderEffect Gaussian blur
Native codeSwift and Objective-CKotlin
How the edge is builtThree system blurs of growing strength per edge, each faded in by a gradient mask (CALayer); paused animators hold each blur at part strengthYour content is recorded once per frame; each edge's strip is blurred at three strengths, each faded in by a gradient mask
Blur strengthUp to about 40 points, the system blur's maximumAny maxBlur
saturation, brightnessNot available; a fixed soft desaturation insteadSupported

On both, the TypeScript component and Reanimated are shared: edge depths and intensity reach the native view from the UI thread, so nothing re-renders while you scroll.

Examples

Crisp until you scroll

The blur fades in over the first 180 points of scroll, so the page is sharp at rest.

const { intensity, onScroll } = useScrollIntensity();

<ProgressiveBlur top intensity={intensity} style={{ flex: 1 }}>
  <Animated.ScrollView onScroll={onScroll} scrollEventThrottle={16}>
    {/* … */}
  </Animated.ScrollView>
</ProgressiveBlur>

A different look on each edge

Shared settings go on the component; each edge overrides only what differs.

<ProgressiveBlur
  tint={background}
  config={{ maxBlur: 20, curve: 'smooth' }}
  top={{ size: insets.top + 64, tintOpacity: 0.3 }}
  bottom={{ size: 300, maxBlur: 40, tintOpacity: 1, softness: 0.5 }}>
  <PaintingGrid />
</ProgressiveBlur>

Fading at both sides of a card

A horizontal gallery that softens into its rounded corners.

<ProgressiveBlur
  left={48}
  right={48}
  config={{ cornerRadius: 20 }}
  style={{ height: 220 }}
>
  <ScrollView horizontal>{/* … */}</ScrollView>
</ProgressiveBlur>

Props

PropTypeDefaultDescription
childrenReactNodeRequiredWhat gets blurred, usually a ScrollView or list.
topEdgeProp—Blur along the top: true, a depth in points, a shared value, or { size, tint, ...look }. The top blurs if no edge is set.
bottomEdgeProp—Blur along the bottom. Same values as top.
leftEdgeProp—Blur along the left. Same values as top.
rightEdgeProp—Blur along the right. Same values as top.
intensitynumber | SharedValue<number>1Scales the blur and the tint, 0–1.
tintColorValue—A colour the edge fades into. Leave it out for a pure blur.
preset"default" | "soft" | "strong""default"The overall look: the standard frost, a lighter blur with a gentle ramp, or a heavy blur that reaches full strength halfway across the band.
configPartial<ProgressiveBlurConfig>—Fine tuning. See Advanced.
styleStyleProp<ViewStyle>—Size it like any view.

Advanced

Everything most apps never need to touch.

Every field is optional and overrides the preset. All but cornerRadius can also be set on a single edge.

FieldDefaultDescription
maxBlur40Blur at the edge, in points. iOS tops out at about 40.
curveS-curveHow the blur grows across the band: "linear", "gentle", "smooth", "smoother", "sharp", "soft", or a cubic Bézier { x1, y1, x2, y2 }.
softness1Share of the band the ramp plays over, 0.05–1. Lower values leave the outer part fully frosted.
tintOpacity0.6How strongly tint shows at the edge, 0–1.
saturation0.9Android only. Saturation of the blurred pixels.
brightness1.03Android only. Brightness of the blurred pixels.
cornerRadius0Rounds and clips the view and the blur together.

  • It blurs its own children, not whatever sits behind it. Wrap the content that should soften, usually the ScrollView, and float headers and buttons over it as siblings.
  • Use config.cornerRadius for rounded corners. style.borderRadius rounds the view but not the blur.
  • Edge depths are in points, so a band looks the same on a phone and a tablet.
  • An edge depth or intensity can be a Reanimated shared value; it updates on the UI thread without a re-render.
  • It runs on iOS 16.4+ and Android, on the New Architecture. There is no web version. See Platforms for what each one uses.
  • It is native code, so rebuild the app after adding it. If the view renders but never blurs, you're on an older build.

  • The blur adds nothing to the accessibility tree, and its children stay readable by screen readers as usual.
  • Nothing moves on its own, so Reduce Motion changes nothing. If your app honours Reduce Transparency, pass a solid tint with tintOpacity: 1.

How it works

It's a local Expo native module for iOS and Android: a small TypeScript component over each platform's own blur, which is why it can soften native views that a JavaScript or Skia blur can't reach. Each blurred edge is a band that stacks three blurs of growing strength, each faded in over its own slice of the curve, so the blur radius grows toward the edge instead of one heavy blur cross-fading in. On iOS these are system blur views behind gradient masks; on Android each edge strip is blurred with RenderEffect. Edge depths and intensity travel to native as animated props, so scrolling never touches React.

Next componentHolographic Sticker