reanimated-skia-performance
Write and review high-performance React Native animations and 2D graphics using react-native-reanimated (v4+) and @shopify/react-native-skia (Canvas scenes, runtime effects/shaders). Use for: gesture-driven interactions, spring/timing transitions, layout/mount animations, Reanimated CSS transitions/animations, Skia drawings, animated shader uniforms, path/vector interpolation, dev-mode tuning panels (sliders), and diagnosing animation jank (JS thread stalls, excessive re-renders, per-frame allocations).
What this skill does
# Reanimated + Skia Performance
## Defaults
- Keep animation state on the UI thread: `useSharedValue`, `useDerivedValue`, worklets.
- Prefer Reanimated v4 declarative APIs; avoid legacy `Animated`.
- Prefer `shared.get()` / `shared.set()` over `shared.value` in app code (React Compiler friendly).
- Minimize JS↔UI crossings: avoid `scheduleOnRN`/`runOnJS` except for unavoidable side effects.
- For Skia, avoid per-frame React renders: pass `SharedValue`s directly to Skia props/uniforms.
## Workflow
1. Define the effect: what animates, duration/curve, interrupt rules, and gesture input.
2. Choose the renderer:
- Use Reanimated styles for transforms/opacity/layout.
- Use Skia for custom drawing, particles, gradients, runtime effects/shaders.
3. Choose the primitive:
- Use Reanimated CSS transitions/animations for simple declarative style changes.
- Use `withTiming` for tweens, `withSpring` for physics, `withDecay` for momentum.
- Use Layout Animations for mount/unmount or layout changes.
4. Implement a single data flow on the UI thread (no `setState` per frame).
5. Do a perf pass (see `references/perf-checklist.md`).
## Patterns
### Shared values (React Compiler safe)
- Read: `progress.get()`
- Write: `progress.set(withTiming(1))`
```ts
import { useEffect } from 'react';
import { useSharedValue, withTiming } from 'react-native-reanimated';
const progress = useSharedValue(0);
useEffect(() => {
progress.set(withTiming(1, { duration: 400 }));
}, [progress]);
```
### Reanimated v4 CSS transitions
Use for state-driven style changes where you do not need bespoke worklets.
```tsx
import Animated from 'react-native-reanimated';
<Animated.View
style={{
width: expanded ? 240 : 160,
opacity: enabled ? 1 : 0.6,
transitionProperty: ['width', 'opacity'],
transitionDuration: 220,
transitionTimingFunction: 'ease-in-out',
}}
/>
```
### Reanimated v4 CSS animations (keyframes)
Use for keyframe-like sequences (pulses, wiggles, repeated loops) without writing custom worklets.
Supported settings:
- `animationName` (keyframes object)
- `animationDuration`
- `animationDelay`
- `animationTimingFunction`
- `animationDirection`
- `animationIterationCount`
- `animationFillMode`
- `animationPlayState`
```tsx
import Animated from 'react-native-reanimated';
const pulse = {
from: { transform: [{ scale: 1 }], opacity: 0.9 },
'50%': { transform: [{ scale: 1.06 }], opacity: 1 },
to: { transform: [{ scale: 1 }], opacity: 0.9 },
};
<Animated.View
style={{
animationName: pulse,
animationDuration: '900ms',
animationDelay: '80ms',
animationTimingFunction: 'ease-in-out',
animationDirection: 'alternate',
animationIterationCount: 'infinite',
animationFillMode: 'both',
animationPlayState: paused ? 'paused' : 'running',
}}
/>
```
### Dev-mode tuning panel (sliders)
Use sliders to tune animation configs or shader uniforms in `__DEV__`.
- Keep the tuning UI in a separate component to avoid re-rendering the animated scene.
- Write slider values into `SharedValue`s via `.set()`.
- For shader tuning: feed `SharedValue`s into uniforms via `useDerivedValue`.
- For animation config tuning: store config params in `SharedValue`s and read them when starting/restarting the animation.
See: `references/dev-tuning.md`.
### Gesture-driven animation (Gesture Builder API)
Update shared values in `onUpdate` and drive visuals via animated styles.
```tsx
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
const x = useSharedValue(0);
const gesture = Gesture.Pan()
.onUpdate((e) => {
x.set(e.translationX);
})
.onEnd(() => {
x.set(withSpring(0));
});
const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.get() }] }));
<GestureDetector gesture={gesture}>
<Animated.View style={style} />
</GestureDetector>;
```
### Skia shader with animated uniforms
- Compile the runtime effect once (`useMemo`).
- Pass uniforms as a `SharedValue<Uniforms>` (Skia detects animated props by `{ value: T }`).
- Do not access `.value` in your app code; pass the `SharedValue` object.
```tsx
import { useMemo } from 'react';
import { Skia, Canvas, Fill, Paint, Shader, type Uniforms } from '@shopify/react-native-skia';
import { useDerivedValue } from 'react-native-reanimated';
import { useClock } from '@shopify/react-native-skia';
const sksl = `
uniform float2 u_resolution;
uniform float u_time;
half4 main(float2 xy) {
float2 uv = xy / u_resolution;
float v = 0.5 + 0.5 * sin(u_time * 0.002 + uv.x * 8.0);
return half4(v, v * 0.6, 1.0 - v, 1.0);
}
`;
const effect = useMemo(() => Skia.RuntimeEffect.Make(sksl), []);
if (!effect) return null;
const clock = useClock(); // ms since first frame
const uniforms = useDerivedValue<Uniforms>(() => ({
u_resolution: Skia.Point(width, height),
u_time: clock.get(),
}));
<Canvas style={{ width, height }}>
<Fill>
<Paint>
<Shader source={effect} uniforms={uniforms} />
</Paint>
</Fill>
</Canvas>;
```
## Common pitfalls
- Do not read shared values in React render; read them in worklets (`useAnimatedStyle`, `useDerivedValue`).
- Do not call `runOnJS`/`scheduleOnRN` in `onUpdate` unless you must.
- Do not allocate big arrays/paths/images per frame; memoize Skia objects and update via animated props.
- For runtime effects, always provide every uniform declared in the shader; missing uniforms throw.
## References
- Reanimated v4 patterns and repo conventions: `references/reanimated-v4.md`
- Skia Canvas + runtime effects/shaders patterns: `references/skia-shaders.md`
- Dev-mode tuning panels (sliders): `references/dev-tuning.md`
- Performance checklist + debugging: `references/perf-checklist.md`
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.