react-native-skia-spoiler
v1.0.0
Published
Telegram-style animated particle spoiler for React Native, powered by Skia + Reanimated
Downloads
138
Maintainers
Readme
react-native-skia-spoiler
Telegram-style animated particle spoiler for React Native — wrap any content (text, images, anything) in <Spoiler> and it's hidden behind a cloud of moving dust until tapped. Built on @shopify/react-native-skia and react-native-reanimated, running fully on the UI thread.
- Works on anything — text, images, icons, a whole card. One component, no per-content-type setup.
- Runs on the UI thread — dust animates via Reanimated worklets, so it keeps moving smoothly even during JS-thread work.
- GPU-drawn with Skia — snapshot, blur, and particles are all composited on the canvas, not layered native views.
- Identical on iOS and Android — the dust, blur, and animation are drawn the same way on both platforms instead of going through separate native APIs, so what you design once looks and behaves the same everywhere — no per-platform tweaking.
- Content-aware dust — particles are seeded from the real pixels of your content, so coverage follows its actual shape instead of a generic grid.
- Controlled or uncontrolled — let it manage its own reveal state, or drive it yourself for custom triggers.
- Tunable look — color, speed, density, and radius are all props; add a blur pass and backing wash for fully opaque photo/video coverage.
How it works
<Spoiler> renders your real children normally, takes a one-time snapshot of them (makeImageFromView), and samples that snapshot's pixels to find exactly where your content is — a word's glyphs, an icon's silhouette, a photo's frame. Because the dust is seeded from the actual pixels of whatever you handed it, it naturally follows the shape of the content underneath — dense over a headline, sparse over whitespace — instead of a uniform grid dropped on top.
Particles are animated on the UI thread via useFrameCallback + useDerivedValue, so wandering dust never triggers a React re-render and stays smooth even while the rest of the screen is busy. Tapping crossfades the dust out and your real content in over Skia's GPU-backed canvas.
Because it works from a snapshot rather than re-drawing text itself, it covers anything you can render in React Native — text, images, icons, whole cards — with the same component and the same handful of props.
Installation
npm install react-native-skia-spoiler @shopify/react-native-skia react-native-reanimated@shopify/react-native-skia and react-native-reanimated are peer dependencies — install and configure them per their own docs if you haven't already (Skia, Reanimated). Skia ships native code, so Expo projects need a development build (expo prebuild / expo run:ios / expo run:android) — it will not work inside plain Expo Go.
Usage
import { Text } from 'react-native';
import { Spoiler } from 'react-native-skia-spoiler';
function Example() {
return (
<Spoiler>
<Text style={{ fontSize: 17 }}>The Winter Soldier is Bucky Barnes</Text>
</Spoiler>
);
}Works the same way around an image:
<Spoiler blurAmount={14} backingOpacity={0.18} style={{ borderRadius: 12, overflow: 'hidden' }}>
<Image source={require('./photo.jpg')} style={{ width: 220, height: 160 }} />
</Spoiler>Controlled reveal
const [revealed, setRevealed] = useState(false);
<Spoiler revealed={revealed} onRevealChange={setRevealed}>
<Text>Snape kills Dumbledore</Text>
</Spoiler>Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| children | ReactNode | — | Anything renderable — text, image, a whole card. |
| revealed | boolean | uncontrolled | Pass to control reveal state yourself; omit to let the component manage it internally. |
| revealOnTap | boolean | true | Whether tapping toggles reveal. |
| onRevealChange | (revealed: boolean) => void | — | Called on every reveal/hide, controlled or not. |
| particleColor | string | '#8E8E93' | Color of the dust particles and the optional backing wash. |
| particleSpeed | number | 1 | Multiplier for how fast particles wander. |
| particleDensity | number | 3 | Grid spacing (px) used to sample content and seed particles. Lower = denser. |
| particleRadius | number | 0.7 | Base particle radius. |
| backingOpacity | number | 0 | Opacity of a solid color wash behind the particles, for full coverage. Off by default — for text, dust alone is enough, like Telegram's text spoiler. Turn on for content that needs full opaque coverage. |
| blurAmount | number | 0 | Gaussian blur sigma applied to the captured content, drawn behind the wash + particles. Off by default; matches how Telegram covers photos/videos — blur first, dust on top. |
| style | ViewStyle | — | Style for the outer wrapper. |
License
MIT
