Pause Reanimated animations and inspect their state at any frame
npm install reanimated-pause-statePause Reanimated animations and capture state snapshots for debugging and design review.
- Pause/resume animations created with Reanimated factory functions
- Capture animation state snapshots with current values, timing, and source locations
- Filter snapshots by component name, file, or line proximity
- Generate prompt-ready markdown for LLM-assisted debugging
Zero overhead in production - all functionality is gated behind __DEV__.
``bash`
npm install reanimated-pause-state
`js`
// babel.config.js
module.exports = {
presets: ['babel-preset-expo'],
plugins: [
'reanimated-pause-state/babel', // MUST be first
'react-native-reanimated/plugin', // MUST be last
],
};
`bash`
npx expo start --clear
`tsx
import { pause, resume, getSnapshotMarkdown } from 'reanimated-pause-state';
import * as Clipboard from 'expo-clipboard';
// In a dev menu, button, or shake handler:
const handlePause = () => {
pause();
const markdown = getSnapshotMarkdown();
Clipboard.setStringAsync(markdown);
Alert.alert('Copied', 'Animation state copied to clipboard');
};
`
Now paste into Claude or your team chat with full animation context.
`tsx
import {
pause,
resume,
toggle,
snapshotNow,
getSnapshotMarkdown,
isInstalled,
isPaused
} from 'reanimated-pause-state';
// Verify setup
console.log('Plugin installed:', isInstalled());
// Pause animations
pause();
// Take a snapshot of current animation state
const snapshot = snapshotNow();
console.log(snapshot);
// {
// timestamp: 1706123456789,
// animations: [
// {
// sharedValueName: 'translateX',
// type: 'repeat',
// values: { from: 0, to: 200, current: 127.5 },
// callsite: { file: 'MyScreen.tsx', line: 42 },
// ...
// }
// ]
// }
// Get markdown for pasting into Claude/LLM
const markdown = getSnapshotMarkdown();
// Resume
resume();
`
| Function | Returns | Description |
|----------|---------|-------------|
| isInstalled() | boolean | Check if Babel plugin is configured |isPaused()
| | boolean | Check if animations are paused |pause()
| | PauseSnapshot | Pause all animations, return snapshot |resume()
| | void | Resume all animations |toggle()
| | PauseSnapshot \| null | Toggle pause state |
#### snapshotNow(opts?: SnapshotOptions): PauseSnapshot
Take a snapshot of current animation state with optional filtering.
`tsx
// All animations
snapshotNow()
// Filter by file
snapshotNow({ filterFile: 'MyScreen.tsx' })
// Filter by file + line proximity
snapshotNow({
filterFile: 'MyScreen.tsx',
filterLine: 42,
proximityThreshold: 200
})
// Limit results
snapshotNow({ maxAnimations: 5 })
`
#### getSnapshotMarkdown(opts?: SnapshotOptions): string
Get snapshot as prompt-ready markdown.
`tsxtranslateX
const markdown = getSnapshotMarkdown({ filterFile: 'MyScreen.tsx' });
// ## Animation State at Pause
//
// ### (repeat)127.50
//
// | Property | Value |
// |----------|-------|
// | Current | |MyScreen.tsx:42
// | From → To | 0 → 200 |
// | Progress | 63.7% |
// | Location | |`
#### getSnapshotJSON(opts?: SnapshotOptions): string
Get snapshot as JSON string for logging or export.
`tsx`
const json = getSnapshotJSON({ filterFile: 'MyScreen.tsx' });
console.log(json);
// {
// "timestamp": 1706123456789,
// "animations": [...],
// "totalCount": 3,
// "summary": "3 animation(s) captured"
// }
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| filterComponent | string | - | Partial match on React component name (case-insensitive) |filterFile
| | string | - | Partial match on callsite file path |filterLine
| | number | - | Center line for proximity filter (requires filterFile) |proximityThreshold
| | number | 200 | Max line distance from filterLine |maxAnimations
| | number | 20 | Maximum animations to return |
`tsx
interface PauseSnapshot {
timestamp: number;
timestampISO: string;
animations: AnimationSnapshot[];
totalCount: number;
summary: string;
}
interface AnimationSnapshot {
id: string;
type: 'timing' | 'spring' | 'decay' | 'repeat' | 'sequence' | 'delay';
sharedValueName?: string; // e.g., 'translateX', 'scale'
componentName?: string; // React component where animation is defined
callsite: {
file: string;
line: number;
column?: number;
};
values: {
from: number;
to: number;
current: number; // actual value at snapshot time
};
timing: {
startTime: number;
elapsed: number;
duration?: number;
progress?: number; // 0-100 for timing animations
};
config: { type: string; config: Record
description: string;
}
`
Find animations defined in a specific React component:
`tsx`
snapshotNow({ filterComponent: 'PulsingCircle' })
Matching is case-insensitive and partial, so 'pulsing' would also match.
Find animations defined in a specific file:
`tsx`
snapshotNow({ filterFile: 'MyScreen.tsx' })
Narrow down to animations near a specific line:
`tsx`
snapshotNow({
filterFile: 'MyScreen.tsx',
filterLine: 42,
proximityThreshold: 200 // within 200 lines
})
1. Babel plugin wraps animation factories (withTiming, withSpring, etc.) to track metadata including component name
2. Runtime captures SharedValue references and registers animations with their source context
3. On pause reads current values from SharedValues (synced from UI thread)
4. Filtering matches by component name (primary), file path, or line proximity
- withTimingwithSpring
- withDecay
- withRepeat
- withSequence
- withDelay
-
For the best animation tracking experience, follow these tips:
The babel plugin captures the enclosing function name. Named components enable accurate filtering.
`tsx
// Good - named component
function PulsingCircle() {
const scale = useSharedValue(1);
// ...
}
// Good - named const
const PulsingCircle = () => {
const scale = useSharedValue(1);
// ...
};
// Less ideal - anonymous inline
export default () => {
const scale = useSharedValue(1);
// ...
};
`
SharedValue variable names appear in snapshots, making debugging easier.
`tsx
// Good - descriptive names
const translateX = useSharedValue(0);
const opacity = useSharedValue(1);
const scale = useSharedValue(1);
// Harder to debug
const sv1 = useSharedValue(0);
const val = useSharedValue(1);
`
The babel plugin tracks the file and line where animations are defined. Keep them together for accurate filtering.
`tsx
// Good - animation near SharedValue
function MyComponent() {
const translateX = useSharedValue(0);
const animate = () => {
translateX.value = withSpring(100); // Line 5
};
}
// Split across files is harder to track
// animation-utils.ts
export const startAnimation = (sv) => {
sv.value = withSpring(100); // Tracked here, not in component
};
`
The plugin MUST run before Reanimated's plugin:
`js`
// babel.config.js
module.exports = {
plugins: [
'reanimated-pause-state/babel', // First!
'react-native-reanimated/plugin', // Last!
],
};
When SharedValues are passed as props, animations are tracked at the call site:
`tsx
function Parent() {
const scale = useSharedValue(1);
return
}
function Child({ scale }) {
const animate = () => {
scale.value = withSpring(2); // Tracked as "Child" component
};
}
`
Annotating Child will show the animation. Annotating Parent won't (the filter won't match, but you'll still see all animations with their SharedValue names).
- Only affects animations created via instrumented factory calls
- Does not pause native animations (Lottie, navigation transitions)
- Dynamic toValue (computed at runtime) won't be captured in config
- Spring animations don't have duration/progress (physics-based)
- React Native 0.70+
- react-native-reanimated >= 3.0.0 (peer dependency)
`json``
// package.json
{
"dependencies": {
"react-native-reanimated": "^3.0.0",
"reanimated-pause-state": "^0.1.0"
}
}
MIT