React Native spoilers.

Hide messages, values, images, or custom views with native particle animations on iOS and Android. Supports controlled state, accessibility, and reduced motion.

Spoiler particles concealing chat messages and values in a bank statement
iOSCore Animation particles
AndroidShared ambient texture work
AccessibleVoiceOver and TalkBack aware
BoundedParticle work capped at runtime

Built for real content

The spoiler effect sits above native sibling content instead of flattening it into a canvas snapshot. Hide a message, a few words, an account number, an image, or an entire custom view.

  • Touch-origin revealA circular reveal begins exactly where the user taps.
  • Ambient motionThree particle depth and opacity tiers move independently.
  • Controlled or localOwn reveal state in the parent or let the component manage it.
  • Safe descendantsHidden content is blocked from touch and accessibility focus.
  • Reduced MotionSystem animation preferences are respected on both platforms.
  • Runtime limitsUnsafe configuration is normalized and particle work stays bounded.

Install

Package and native peers

npm install react-native-spoiler-view \
  react-native-reanimated \
  react-native-gesture-handler

For Reanimated 4, also install react-native-worklets and put its Babel plugin last.

npm install react-native-worklets
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['react-native-worklets/plugin'],
};

Choose peer dependency versions compatible with your React Native version. Reanimated 4 requires the New Architecture; Reanimated 3 remains the supported legacy lane.

Compatibility

LaneReact NativeReactReanimatedGesture Handler
Legacy0.73.x18.x3.15.52.18.x
Current0.86.x19.x4.5.x + Worklets 0.10.x3.x

Use it

Uncontrolled

import { Text } from 'react-native';
import { SpoilerView } from 'react-native-spoiler-view';

<SpoilerView accessibilityRevealLabel="Hidden message">
  <Text>This is a secret message.</Text>
</SpoilerView>

The component owns its state. Tapping toggles between hidden and revealed.

Controlled

const [revealed, setRevealed] = useState(false);

<SpoilerView
  revealed={revealed}
  onReveal={() => setRevealed(true)}
  onHide={() => setRevealed(false)}
  accessibilityRevealLabel="Hidden account number"
>
  <Text>1234 5678 9012 3456</Text>
</SpoilerView>

Callbacks request the change; the component animates only when the controlled prop changes.

Custom particles

<SpoilerView
  config={{
    particleCount: 300,
    particleColor: 'rgba(255, 100, 100, 1)',
    particleSizeRange: [0.5, 1.5],
    revealDuration: 400,
  }}
>
  <Image source={secretImage} />
</SpoilerView>

API

PropTypeDefaultDescription
childrenReactNoderequiredContent hidden by the spoiler
revealedbooleanuncontrolledParent-owned reveal state
enabledbooleantrueEnables reveal and hide gestures
onReveal() => voidReveal request callback
onHide() => voidHide request callback
configPartial<SpoilerConfig>Particle and animation overrides
styleStyleProp<ViewStyle>Container style

Default particle configuration

{
  particleCount: 180,
  particleDensity: 0.055,
  particleSizeRange: [0.45, 0.8],
  particleColor: 'rgba(80, 80, 80, 1)',
  overlayColor: 'transparent',
  noiseSpeed: 0.3,
  driftAmount: 1,
  revealDuration: 500,
}

Particle count is capped at 1,000. Providing particleCount without particleDensity opts into a fixed target; otherwise density adapts the effect to the spoiler area.

Read the complete API and accessibility reference on GitHub →