React Native Text Animation: Reveals and Readable Copy
By Native Animation · Updated
A React Native text animation should preserve the words people need to read while animating how they appear. Use a normal Text element with Reanimated opacity and translation for meaningful copy; reserve character scrambling and split-flap glyphs for a visual treatment with a stable accessible label. This guide builds an interruptible reveal, then compares three catalog patterns.
Choose semantic text or decorative glyphs
A headline, error, price, or instruction is content. Keep its complete string in the native text tree, even while the presentation moves. A decorative glyph layer can look like text without behaving like a paragraph: splitting every character into Views changes wrapping, selection, and screen-reader output.
| Task | Starting point | Keep stable |
|---|---|---|
| Reveal a heading or short paragraph | Animate a wrapper around Text | Full string and reading order |
| Scramble into a short phrase | Decrypt Text | Final accessible label |
| Illuminate words while reading | Scroll Word Reveal | Paragraph meaning and font-scale layout |
| Show a departure-board transition | Split-Flap Board | Trip identity and final values |
| Animate a changing numeric value | Animated counter guide | Numeric value and units |
Do not make an error message wait for a typewriter effect. If a state change needs an announcement, announce the final message once through the host's accessibility policy, never each scrambled glyph. A readable label and a live announcement solve different problems.
Build an interruptible text reveal
This complete example targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, and Worklets 0.13.0. Install with npx expo install react-native-reanimated react-native-worklets (bunx expo install in a Bun project), following the versioned setup. Save it as TextRevealExample.tsx outside your Expo Router routes and render it in a safe-area screen.
import { useEffect, useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import Animated, {
cancelAnimation, useAnimatedStyle, useReducedMotion,
useSharedValue, withTiming,
} from 'react-native-reanimated';
type RevealProps = { text: string; visible: boolean };
export function TextReveal({ text, visible }: RevealProps) {
const reduced = useReducedMotion();
const progress = useSharedValue(1);
useEffect(() => {
progress.set(reduced ? Number(visible) : withTiming(Number(visible), { duration: 240 }));
return () => cancelAnimation(progress);
}, [visible, progress, reduced]);
const animatedStyle = useAnimatedStyle(() => ({
opacity: progress.get(),
transform: [{ translateY: reduced ? 0 : 8 * (1 - progress.get()) }],
}));
return (
<View style={styles.copy}>
<Animated.View style={animatedStyle}>
<Text selectable style={styles.text}>{text}</Text>
</Animated.View>
</View>
);
}
export function TextRevealExample() {
const [visible, setVisible] = useState(true);
const [alternate, setAlternate] = useState(false);
return (
<View style={styles.screen}>
<TextReveal visible={visible} text={alternate
? 'Your next chapter can start with a small change.'
: 'Make room for a slower morning.'} />
<Text style={styles.note}>Fade changes presentation. The copy stays available to assistive technology.</Text>
<Button title={visible ? 'Fade out' : 'Reveal text'} onPress={() => setVisible((value) => !value)} />
<Button title="Change copy" onPress={() => setAlternate((value) => !value)} />
</View>
);
}
const styles = StyleSheet.create({
screen: { padding: 24, gap: 20, backgroundColor: '#FFFFFF' },
copy: { minHeight: 112 },
text: { color: '#231D2B', fontSize: 28, lineHeight: 36 },
note: { color: '#494151', fontSize: 16, lineHeight: 24 },
});Repeated updates and interruption
Tap Fade out and Reveal text before the transition finishes. Each request retargets the same shared value from its current position; there is no completion timer that can erase a newer phrase. Changing copy updates the string immediately and leaves the current fade in progress. The example does not crossfade old and new sentences or reset progress to zero on each text update.
The words start fully visible on mount. That avoids a blank first render while the accessibility preference is read. This is an on-demand reveal example rather than an entrance animation that hides essential copy before the screen is usable.
Keep wrapping, selection, and reading order
Long text and font scaling
The Text element wraps as one paragraph, and the wrapper animates without fixing its height. The minimum height reserves some space for this demo; it does not cap longer content. Test a narrow container, a long translation, emoji, and the largest text size your app supports. Leave font scaling enabled.
A character array built with split('') can split emoji and combining sequences. Even grapheme-aware segmentation does not automatically provide correct shaping for Arabic or other scripts. Animate an intact text block unless separate glyphs are a deliberate display treatment with a tested language scope.
Selection and hidden content
The demo's Text remains selectable. Fading out is purely visual: the string remains available to assistive technology. Do not use this wrapper to hide confidential content or collapse a subtree of controls. For actual disclosure, coordinate hidden state, focus, and layout as in the accordion guide.
Choose a catalog text animation
Decrypt Text takes text, a mode of reveal, random, or wave, and timing controls such as settleMs and staggerMs. Its wrapper exposes the final phrase; the animated input and glyph layers are hidden from assistive technology. It is a scramble treatment, not a password-encryption function. After copying the folder next to your screen:
import DecryptText from './decrypt-text/DecryptText';
export function SignalHeading() {
return <DecryptText text="SIGNAL ACQUIRED" mode="wave" settleMs={460} staggerMs={34} />;
}Scroll Word Reveal is an editorial screen with measured words, a fixed header, and a final image. It requires imageSource; its paragraphs use { text, highlightedWords }. It owns a scrolling presentation, so it is a better fit for a story screen than an inline form label. Use its scroll-linked behavior instead of inventing a duration prop.
Split-Flap Board accepts departures with stable id, time, destination, gate, and status fields. Its onRowPress and ref methods support a trip-screen treatment. It is not a general rich-text renderer. Keep the application data authoritative and let the board visualize updates.
Each published piece is reviewed on physical iPhone and Android devices with Expo Go before release. Read the piece README for its dependencies, public API, and customization limits. The CLI guide covers adding the source to your project.
Accessibility and reduced motion
The demo's useReducedMotion reads the startup preference and removes translation and timed fading when enabled. It does not subscribe to changes made while the app is open. For live changes, subscribe to AccessibilityInfo's reduceMotionChanged event in a shared host hook, then cancel active motion and apply the current requested state.
Keep decorative duplicates out of the accessibility tree. Test native reading order and web selection separately; a stable accessible name does not guarantee that a visual glyph composition offers paragraph selection. For continuously changing data, avoid a live region that announces every animation tick.
FAQ
How do I animate text in React Native?
Render the complete string in Text, then animate a wrapper's opacity and transform with Reanimated. Keep the data update independent of animation completion so rapid changes cannot restore stale copy.
Do I need Skia for text animation?
No. A basic reveal needs only Text and Reanimated. Use custom drawing when the visual treatment needs masks, shaders, or shapes; account for fonts, accessibility, and web initialization separately.
Can a scramble effect stay accessible?
Yes. Expose the final phrase once and hide the changing decorative glyphs. Decrypt Text uses that model. Announce a final status only when the product context calls for it.
Should I animate every character in a paragraph?
Usually a block reveal or a measured word treatment is easier to read and maintain. Per-character layout needs extra work for wrapping, shaping, emoji, selection, and font scaling.